Operator overloading

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sat Dec 27 10:04:10 PST 2008


Don wrote:
> Andrei Alexandrescu wrote:
>> Bill Baxter wrote:
>>> On Sat, Dec 27, 2008 at 9:42 AM, The Anh Tran <trtheanh at gmail.com> 
>>> wrote:
>>>> aarti_pl wrote:
>>>>> Andrei Alexandrescu pisze:
>>>>>  > We're trying to make that work. D is due for an operator overhaul.
>>>>>  >
>>>>>  > Andrei
>>>>>
>>>>> Is there any chance that we get possibility to overload "raw 
>>>>> operators",
>>>>> like in C++? I think that they may coexist with currently defined 
>>>>> operator
>>>>> overloads with simple semantic rules, which will not allow them to 
>>>>> work
>>>>> together at the same time.
>>>>> ..........
>>>>> BR
>>>>> Marcin Kuszczak
>>>>> (aarti_pl)
>>>> Me also have a dream :D
>>>>
>>>> <Daydream mode>
>>>> class Foo
>>>> {
>>>>        auto op(++)(); // bar++
>>>>        auto op(++)(int); // ++bar
>>>>
>>>>        op(cast)(uint); // cast(uint)bar // opCast
>>>>        auto op(())(int, float); // Foo(123, 123.456) // opCall
>>>>
>>>>        auto op(+)(Foo rhs); // bar1 + bar2
>>>>        auto op(+=)(int); // bar += 1234;
>>>>        auto op(.)(); // bar.xyz // opDot
>>>>
>>>>        Foo op([][][])(int, char, float); // bar[123]['x'][123.456]
>>>>
>>>>        auto op([..])(); // i = bar2[] // opSlide
>>>>        auto op([..])(int, int); // bar[1..10]
>>>>
>>>>        auto op([..]=)(float); // bar[] = 12.3 //opSlideAssign
>>>>        auto op([..]=)(int, int, float); // bar[1..3] = 123.4
>>>> }
>>>> </Dream>
>>>
>>> When I suggested this kind of thing long ago, Walter said that it
>>> encourages operator overload abuse, because it suggests that  + is
>>> just a generic symbolic operator rather than something that
>>> specifically means "addition".  That's why D uses "opAdd" instead.
>>> It's supposed to encourage only creating overloads that follow the
>>> original meaning of the operator closely.  That way when you see a+b
>>> you can be reasonably sure that it means addition or something quite
>>> like it.
>>
>> I think that argument is rather weak and ought to be revisited. It's 
>> weak to start with as if writing "+" in a D program hardly evokes 
>> anything else but "plus". What the notation effectively achieved was 
>> put more burden on the programmer to memorize some names for the 
>> already-known symbols. I think the entire operator overloading 
>> business, which started from a legitimate desire to improve on C++'s, 
>> ended up worse off.
> 
> I feel quite strongly that C++'s operator overloading was a failed 
> experiment. The original intention (AFAIK) was to allow creation of 
> mathematical entities which could use natural syntax. The classic 
> example was complex numbers, and it works reasonably well for that, 
> although it requires you to create an absurd number of repetitive 
> functions.
> 
> But for anything much more complicated, such as matrices, tensors, big 
> integer arithmetic, etc -- it's an abject failure. It's clumsy, and 
> creates masses of temporary objects, which kills performance so 
> completely that it's unusable. But the whole point of operator 
> overloading was to allow nice notation in a performace-oriented 
> language! Expression templates are basically a hack to restore 
> performance in most cases, but it comes at a massive cost in simplicity.
> And the performance even then is not always optimal.
> 
> I think that Walter's idea, in tightening the semantics of overloaded 
> operators, is the right approach. Unfortunately, it doesn't go far 
> enough, so we get the worst of both worlds: the C++ freedom is 
> curtailed, but there isn't enough power to replace it.

Very well put.

> Ultimately, I think that the problem is that ideally, '+' is not simply 
> a call to a function called 'plus()'. What you'd like an operator to 
> compile to, depends on the expression in which it is embedded. For 
> maximum performance, an expression needs to be digested before it is 
> converted into elementary functions.
> 
> In my 'operator overloading without temporaries' proposal in Bugzilla,
> I showed that DEFINING a -= b as being identical to a = a - b, and then 
>  creating a symmetric operation for a = b - a allows optimal code 
> generation in a great many cases. It's not a complete solution, though.
> 
> In particular, irreducible temporaries need more thought. Ideally, in 
> something like a += b * c + d, b*c would be created in a memory pool, 
> and deleted at the end of the expression.
> (By contrast, a = b*c+d, would translate to a=b*c; a+=d; so no temporary 
> is required).

That's an awesome proposal. I'd like to expand it to comprehend fusion 
as well. Consider:

A = B + C - D;

where the operands are matrices. The best hand-written implementation 
would loop once through the three matrices and assign to the destination 
element-wise A[i, j] = B[i, j] + C[i, j] - D[i, j]. However, with an 
approach that has only one operator application as its horizon, it is 
impossible to achieve that optimization. So I wonder what abstraction 
could be devised that makes it easy and natural to support such fusion. 
Expression templates achieve that by saving the right-hand expression 
tree as a type and then using it during the assignment. This requires a 
considerable effort and has some drawbacks.

> There are other, less serious problems which also need to be addressed.
> 
> Defining ++a as a+=1 is probably a mistake. It raises lots of nasty issues.
> * If a is a complex number, a = a + 1 makes perfect sense. But it's not 
> obvious that ++a is sensible.
> * What type is '1'? Is it an int, a uint, a long, ... You don't have 
> that issue with increment.

Great points!

> As I see it, there are two possible strategies:
> (1) Pursuing optimal performance, which requires semantic tightening, 
> and reduced flexibility, or
> (2) Pursure simplicity and semantic flexibility, sacrificing performance.
> 
> I think those two possibilities are mutually exclusive.

I tend to be more optimistic, but if asked to choose, I'd go for (1). 
One important lesson learned from C++'s operator overloading is that 
freedom was almost always badly used. Tellingly, whenever operator 
overloading is taught or talked about, the first caveat mentioned is 
that defining inconsistent batteries of operators is exceedingly easy.

Andrei



More information about the Digitalmars-d mailing list