Declaration and Use of Overloaded Operators


What s Different in the Revised
Language Definition?

Support for Operator Overloading


 


 

Perhaps the most baffling aspect of the original
language design is its support for operator overloading or rather, its effective absence.
As an outsider, I took it as evidence of an abandonment, and felt this absolutely
required fixing if Microsoft was really serious in its commitment to C++. Well, the
good news is that the support for operator overloading has been fixed (bringing with
it all those things implicit in its support). Although it can be shrugged off as just syntax,
I have always felt it to be a litmus test of a revised language design yes, more so
even than finalistic determination.


 

Within the declaration of a reference type, for example,
rather than using the native
operator+ syntax,
one had to explicitly write out the underlying internal name of the operator in this
case,
op_Addition.
More onerous, however, is the fact that the invocation of an operator had to be explicitly
invoked through that name, thus precluding the two primary benefits of operator overloading:
(a) the intuitive syntax, and (b) the ability to intermix new types with existing
types. For example,

public __gc __sealed class Vector

{

public:

     Vector( double x, double y, double z
);

      

     static bool    op_Equality( const Vector*, const Vector*
);

     static Vector*
op_Division( const Vector*, double );

     static Vector*
op_Addition( const Vector*, const Vector*
);

     static Vector*
op_Subtraction( const Vector*, const Vector*
);

};


 

int main()

{

       Vector
*pa = new Vector( 0.231, 2.4745, 0.023 );

       Vector
*pb = new Vector( 1.475, 4.8916, -1.23 );


 

       Vector
*pc1 = Vector::op_Addition( pa, pb );

       Vector
*pc2 = Vector::op_Subtraction( pa, pc1 );

       Vector
*pc3 = Vector::op_Division( pc1, pc2->x() );


 

       if (
Vector::op_Equality( pc1, p2 ))

            //
&

}

In the language revision, the usual conventions are
restored. Nothing really surprising here one could almost call the change boring,
but in this case, that is a good thing. Well, the only surprising thing, which is
an aspect of the underlying CLR, is the absence of a static subscript operator, which
is factored out into what in C++/CLI is referred to as an indexed property. But this
is a good thing as well, but that is for another entry.

public ref class Vector
sealed

{

public:

       Vector( double x, double y, double z
);


 

       static bool    operator ==( const Vector^, const Vector^
);

       static Vector^ operator /( const Vector^, double );

       static Vector^ operator +( const Vector^, const Vector^
);

       static Vector^ operator -( const Vector^, const Vector^
);

};


 

int main()

{

       Vector^
pa = gcnew Vector( 0.231, 2.4745, 0.023 ),

       Vector^
pb = gcnew Vector( 1.475,4.8916,-1.23 );


 

       Vector^
pc1 = pa + pb;

       Vector^
pc2 = pa pc1;

       Vector^
pc3 = pc1 / pc2->x();


 

       if (
pc1 == p2 )

            //
&


}

The purpose of this series of blog entries is to
document the changes between the original and revised language design (I should probably
just designate these as Thing1 (T1) and Thing2 (T2) and be done with it) rather than
address the many new and innovative language features (such as the support for non-static
operators within a managed class). For that, I recommend you check out (a) the blogs
of Herb Sutter and
Brandon Bray
or (b) download the C++/CLI Candidate Base Document [sorry, I don t have the URL handy],
or (c) both of the above. However, I would like to break that rule in one small case.


 

One of the petty annoyances of .NET is its lack of
support for default arguments. For example, a Vector class would like to support four
varieties of object initialization, as follows:


 

int main()

{

       Vector^
p1 = gcnew Vector;

       Vector^
p2 = gcnew Vector( 1.475 );

       Vector^
p3 = gcnew Vector( p2[ 0 ], 2.4745 );

       Vector^
p4 = gcnew Vector( p3[ 1 ], p2[ 0 ], 3.14159 );

}


 

In native C++, one could do that with a single constructor
with a default argument of zero for each coordinate parameter. Under the constraint
of .NET, one must provide four separate constructors, the signature of each mapping
to the successive constructor invocations in the above example. C# provides support
for a delegating constructor, which relieves the programmer from having to implement
all four instances. Rather, one delegates values to one implementing constructor.
T2 has added this to its feature list:


 

public ref class Vector
sealed

{

       double
m_x, m_y, m_z;

public:

       Vector()
: Vector( 0.0, 0.0, 0.0 ){}

Vector( double x
) : Vector( x, 0.0, 0.0 ){}

Vector( double x, double y
) : Vector( x, y, 0.0 ){}

       Vector( double x, double y, double z
) : m_x( x ), m_y( y ), m_z( z ){}


 

       //
&

};


 

disclaimer: This posting is
provided “AS IS” with no warranties, and confers no rights. 
    

           


Comments (3)

  1. Albert Szilvasy says:

    Thanks for the valuable insights!
    I have comment that must have been made before but I would appreciate if you could comment on it. Delegating constructors will be great but why not take this a step further and actually autogenerate the overloads when default parameters are used?
    This could apply to both constructors and other functions. The above example could simply be written as follows:

    public ref class Vector sealed

    {

    double m_x, m_y, m_z;

    public:

    Vector( double x = 0.0, double y = 0.0, double z = 0.0 ) : m_x( x ), m_y( y ), m_z( z ){}
    // …
    };

    The compiler could generate all overloads automatically, couldn’t it?

    albert

  2. Anonymous says:

    Yeah but how many classes could take advantage of that in general? I’d imagine slim to none.

  3. stan lippman says:

    I’m reminded of when people use to ask, why do you have positional default arguments when named default arguments would prove more flexible? When I worked on the original prototype of a revised language, from November through March of 2001-2002, i considered this briefly and decided it was not sufficient payback for veneering over CLR policy. I used C# for about a year, although not with any production level code, and i did not find the absence of default arguments that onerous.