www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Re: user defined implicit casts

reply Ender KaShae <astrothayne gmail.ckom> writes:
Jarrett Billingsley Wrote:

 "Tristam MacDonald" <swiftcoder gmail.com> wrote in message 
 news:f8dndh$1c8l$1 digitalmars.com...
 While I would like to see this, I wopuld like to see a logical extension 
 of the last part even more: non-member operator overloads. This is an 
 incredibly neat feature from C++ that was lost somewhere along the way, 
 but I don't know if it was intentional or not...

It was intentional. The problem with global or static (as in C#) operator overloads is that they cannot be virtual, and so you end up writing the non-virtual operator overloads which simply call a virtual implementation method on the object(s), which is pointless -- just make the overload virtual in the first place, and it works fine.

c++ has member, global, and static operator overloads it is up to the programmer to choose how to implement it, I think it would be nice if D had a similar mechanizm.
Jul 30 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Ender KaShae" <astrothayne gmail.ckom> wrote in message 
news:f8m0k6$2oo9$1 digitalmars.com...
 c++ has member, global, and static operator overloads it is up to the 
 programmer to choose how to implement it, I think it would be nice if D 
 had a similar mechanizm.

Can you come up with something more solid than "this is how C++ does it?" Can you explain what functionality global and static operator overloads provide that member operator overloads cannot?
Jul 30 2007
parent reply Reiner Pope <some address.com> writes:
Jarrett Billingsley wrote:
 "Ender KaShae" <astrothayne gmail.ckom> wrote in message 
 news:f8m0k6$2oo9$1 digitalmars.com...
 c++ has member, global, and static operator overloads it is up to the 
 programmer to choose how to implement it, I think it would be nice if D 
 had a similar mechanizm.

Can you come up with something more solid than "this is how C++ does it?" Can you explain what functionality global and static operator overloads provide that member operator overloads cannot?

non-member functions are useful: writing functions that operate on a type, but don't belong within that type. I can think of one example where global operator overloads make sense: // stores a list of T, with opIndex, opCat, etc class List(T) { ... } In this case it might be reasonable to define a function which does a elementwise add of two List!(int)s to produce a third: List!(int) opAdd(List!(int) a, List!(int) b) { ... } But this operator clearly doesn't belong in the List class, as in the general case, you can't add two lists together. -- Reiner
Jul 30 2007
next sibling parent Tristam MacDonald <swiftcoder gmail.com> writes:
It would be nice if this could be generalised to functions, rather than just
operator overloads. Then we could define member functions for specific template
instantiations without having to specialise the whole template, or clog the
body with static ifs.

Reiner Pope Wrote:
 Jarrett Billingsley wrote:
 "Ender KaShae" <astrothayne gmail.ckom> wrote in message 
 news:f8m0k6$2oo9$1 digitalmars.com...
 c++ has member, global, and static operator overloads it is up to the 
 programmer to choose how to implement it, I think it would be nice if D 
 had a similar mechanizm.

Can you come up with something more solid than "this is how C++ does it?" Can you explain what functionality global and static operator overloads provide that member operator overloads cannot?

non-member functions are useful: writing functions that operate on a type, but don't belong within that type. I can think of one example where global operator overloads make sense: // stores a list of T, with opIndex, opCat, etc class List(T) { ... } In this case it might be reasonable to define a function which does a elementwise add of two List!(int)s to produce a third: List!(int) opAdd(List!(int) a, List!(int) b) { ... } But this operator clearly doesn't belong in the List class, as in the general case, you can't add two lists together. -- Reiner

Jul 31 2007
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Reiner Pope" <some address.com> wrote in message 
news:f8m84m$30eq$1 digitalmars.com...
 I can think of one example where global operator overloads make sense:

 // stores a list of T, with opIndex, opCat, etc
 class List(T) { ... }

 In this case it might be reasonable to define a function which does a 
 elementwise add of two List!(int)s to produce a third:

 List!(int) opAdd(List!(int) a, List!(int) b) { ... }

 But this operator clearly doesn't belong in the List class, as in the 
 general case, you can't add two lists together.

Can you generalize that to a List!(any integer type)? What if (I know the chances are slim but bear with me) you created a class derived from List which wanted to change the behavior of opAdd? And is there anything that you absolutely cannot do with member operator overloads, or problems which static/global operator overloads solve more elegantly than member operator overloads?
Jul 31 2007
parent reply Reiner Pope <some address.com> writes:
Jarrett Billingsley wrote:
 Can you generalize that to a List!(any integer type)?  

Not quite, but that's D's fault. The following very nearly works, though, but it doesn't have a specialization on T. If it did, IFTI would break (which, as far as I see, is D's fault, and not something fundamental). List!(T) opAdd(T)(List!(T) a, List!(T) b) { ... } But anyway, the same problem holds if you want to write a templated accumulate function. The problem is in template specialization, not in the global operator overloading.
 What if (I know the 
 chances are slim but bear with me) you created a class derived from List 
 which wanted to change the behavior of opAdd?  

I hadn't thought of this situation. But I don't see anything wrong with the most obvious solution: if a class C has an operator overloaded, for instance opAdd, then only the members are considered in overloading for C.opAdd. If not, then consider globals. I think this situation is very similar to the pseudo-member syntax that many people (including me) are keen on, to generalize the implicit properties for arrays to all types. The same problem arises there, and I would suggest the same solution.
 And is there anything that 
 you absolutely cannot do with member operator overloads, or problems which 
 static/global operator overloads solve more elegantly than member operator 
 overloads? 
 

I think the example I gave is one such. Supposing that there is a way to generalise my example to any iteratable list of any numeric type, then I would say that making it a global is the best design. It makes the algorithm composable, independent of the underlying type (like STL). The alternative is to write: template OpAddImpl(T, ThisType) { static if (IsAddable!(T)) { ThisType opAdd(ThisType other) { ... } } } class List(T) { ... mixin OpAddImpl!(T, typeof(this)); } class Vector(T) { ... mixin OpAddImpl!(T, typeof(this)); } // and the same for all the other list-looking classes You could also try to use inheritance, but then you lose information about the return type, and it's also not currently possible with interfaces since they don't allow implementing even final member functions. -- Reiner
Jul 31 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Reiner Pope" <some address.com> wrote in message 
news:f8oct2$7le$1 digitalmars.com...
 I think this situation is very similar to the pseudo-member syntax that 
 many people (including me) are keen on, to generalize the implicit 
 properties for arrays to all types. The same problem arises there, and I 
 would suggest the same solution.

Hm. I don't really like the idea of just allowing static/global operator overloads in isolation, but if they were allowed as another aspect of this generalized pseudo-member syntax, it would make much more sense.
 I think the example I gave is one such. Supposing that there is a way to 
 generalise my example to any iteratable list of any numeric type, then I 
 would say that making it a global is the best design. It makes the 
 algorithm composable, independent of the underlying type (like STL). The 
 alternative is to write:

 template OpAddImpl(T, ThisType)
 {
     static if (IsAddable!(T))
     {
         ThisType opAdd(ThisType other)
         {
             ...
         }
     }
 }

 class List(T)
 {
     ...
     mixin OpAddImpl!(T, typeof(this));
 }

 class Vector(T)
 {
     ...
     mixin OpAddImpl!(T, typeof(this));
 }

 // and the same for all the other list-looking classes

I see what you're getting at now, and I agree that that would be very useful.
Jul 31 2007