www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - user defined implicit casts

reply Ender KaShae <astrothayne gmail.com> writes:
I have become aware that in D it is possible to create wrapper classes that can
intermingle with primitave types for example:

/**
 * wrappers for all the basic types (and derived types) of D
 * note: these wrappers are mutable
 */
module dragon.math.basictypes;
import dragon.math.numbers;
import std.math;
import std.string;

/// wrapper for byte
class Byte: numeric!(Byte){
	private byte value; /// value of instance
	public:
		this(byte b = 0){ value = b;}	///Constructor
		byte Value() {return value;}	///Returns: value of instance
		void Value(byte b) {value = b;}	///Params: b = new value
		byte opCast() {return value;} /// cast overload
		char[] toString() { return std.string.toString(value);}
		uint toHash() {return abs(value);}

		// binary operator TODO add documentation
		Byte opAdd(Byte b) { return new Byte(value + b.value);}
		Byte opAdd(byte b) { return new Byte(value + b);}
		Byte opSub(Byte b) { return new Byte(value - b.value);}
		Byte opSub(byte b) { return new Byte(value - b);}
		Byte opSub_r(byte b) { return new Byte(b - value);}
		Byte opMul(Byte b) { return new Byte(value * b.value);}
		Byte opMul(byte b) { return new Byte(value * b);}
		Byte opDiv(Byte b) { return new Byte(value / b.value);}
		Byte opDiv(byte b) { return new Byte(value / b);}
		Byte opDiv_r(byte b) {return new Byte(b / value);}
		Byte opMod(Byte b) {return new Byte(value % b.value);}
		Byte opMod(byte b) {return new Byte(value % b);}
		Byte opMod_r(byte b) { return new Byte(b % value);}
		Byte opAnd(Byte b) { return new Byte(value & b.value);}
		Byte opAnd(byte b) { return new Byte(value & b);}
		Byte opOr(Byte b) { return new Byte(value | b.value);}
		Byte opOr(byte b) { return new Byte(value | b);}
		Byte opXor(Byte b) { return new Byte(value ^ b.value);}
		Byte opXor(byte b) { return new Byte(value ^ b);}
		Byte opShl(int i) { return new Byte(value << i);}
		long opShl_r(long l) { return l << value;}
		Byte opShr(int i) {return new Byte(value >> i);}
		long opShr_r(long l) { return l >> value;}
		Byte opUShr(int i) {return new Byte(value >>> i);}
		long opUShr(long l) {return l >>> value;}
		//comparison operators
		int opEquals(Object o) {
			if(o is Byte) {
				Byte b = cast(Byte)o;
				return value == b.value;
			}
			return o == value;
		}
		int opEquals(long l) {
			return l = value;
		}
		int opCmp(Object o) {
			if(o is Byte) {
				Byte b = cast(Byte)o;
				if(value > b.value)return 1;
				if(value < b.value)return -1;
				return 0;
			}
			return o.opCmp(value);
		}
		int opCmp(long l) {
			if(value < l)return -1;
			if(value > l)return 1;
			return 0;
		}
		//assignment operators
		Byte opAssign(byte b) {
			value = b;
			return this;
		}
		Byte opAddAssign(Byte b) {
			value += b.value;
			return this;
		}
		Byte opAddAssign(byte b) {
			value += b;
			return this;
		}
		Byte opSubAssign(Byte b) {
			value -= b.value;
			return this;
		}
		Byte opSubAssign(byte b) {
			value -= b;
			return this;
		}
		Byte opMulAssign(Byte b) {
			value *= b.value;
			return this;
		}
		Byte opMulAssign(byte b) {
			value *= b;
			return this;
		}
		Byte opDivAssign(Byte b) {
			value /= b.value;
			return this;
		}
		Byte opDivAssign(byte b) {
			value /= b;
			return this;
		}
		Byte opModAssign(Byte b) {
			value %= b.value;
			return this;
		}
		Byte opModAssign(byte b) {
			value %= b;
			return this;
		}
		Byte opAndAssign(Byte b) {
			value &= b.value;
			return this;
		}
		Byte opAndAssign(byte b) {
			value &= b;
			return this;
		}
		Byte opOrAssign(Byte b) {
			value |= b.value;
			return this;
		}
		Byte opOrAssign(byte b) {
			value |= b;
			return this;
		}
		Byte opXorAssign(Byte b) {
			value ^= b.value;
			return this;
		}
		Byte opXorAssign(byte b) {
			value ^= b;
			return this;
		}
		Byte opShlAssign(Byte b) {
			value <<= b.value;
			return this;
		}
		Byte opShlAssign(byte b) {
			value <<= b;
			return this;
		}
		Byte opShrAssign(Byte b) {
			value >>= b.value;
			return this;
		}
		Byte opShrAssign(byte b) {
			value >>= b;
			return this;
		}
		Byte opUShrAssign(Byte b) {
			value >>>= b.value;
			return this;
		}
		Byte opUShrAssign(byte b) {
			value >>>= b;
			return this;
		}

		// unary operators TODO add documentation
		Byte opNeg() {return new Byte(-value);}
		Byte opPos() {return new Byte(+value);}
		Byte opCom() {return new Bye(~value);}
		Byte opPostInc() {
			scope temp = value;
			value++;
			return new Byte(temp);
		}
		Byte opPostDec() {
			scope temp = value;
			value--;
			return new Byte(temp);
		}
}

now the problem is a lack of implicit casting for user defined types the Byte
cannot be implicitly cast to byte, int, long etc. even though such a cast would
make sense.  If implicit casting were allowed then a library could be fashioned
with wrapper for all of the basic types and some less basic types such as cint,
iint, fraction, radical, ifraction, cfraction, etc.  

my suggested syntax for overloading implicit casts is:

implicit type1 cast(type2 var);

where type1 is the type casting to and type2 is the type casting from
this syntax would allow non-member functions to be used, so that implicit casts
can be overloaded in specific contexts 
example:
implicit int cast(long var){ return cast(int)var;}
when you know that longs will never exceed the bound of ints
Jul 27 2007
next sibling parent reply Tristam MacDonald <swiftcoder gmail.com> writes:
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...

Ender KaShae Wrote:
 I have become aware that in D it is possible to create wrapper classes that
can intermingle with primitave types for example:
 
 /**
  * wrappers for all the basic types (and derived types) of D
  * note: these wrappers are mutable
  */
 module dragon.math.basictypes;
 import dragon.math.numbers;
 import std.math;
 import std.string;
 
 /// wrapper for byte
 class Byte: numeric!(Byte){
 	private byte value; /// value of instance
 	public:
 		this(byte b = 0){ value = b;}	///Constructor
 		byte Value() {return value;}	///Returns: value of instance
 		void Value(byte b) {value = b;}	///Params: b = new value
 		byte opCast() {return value;} /// cast overload
 		char[] toString() { return std.string.toString(value);}
 		uint toHash() {return abs(value);}
 
 		// binary operator TODO add documentation
 		Byte opAdd(Byte b) { return new Byte(value + b.value);}
 		Byte opAdd(byte b) { return new Byte(value + b);}
 		Byte opSub(Byte b) { return new Byte(value - b.value);}
 		Byte opSub(byte b) { return new Byte(value - b);}
 		Byte opSub_r(byte b) { return new Byte(b - value);}
 		Byte opMul(Byte b) { return new Byte(value * b.value);}
 		Byte opMul(byte b) { return new Byte(value * b);}
 		Byte opDiv(Byte b) { return new Byte(value / b.value);}
 		Byte opDiv(byte b) { return new Byte(value / b);}
 		Byte opDiv_r(byte b) {return new Byte(b / value);}
 		Byte opMod(Byte b) {return new Byte(value % b.value);}
 		Byte opMod(byte b) {return new Byte(value % b);}
 		Byte opMod_r(byte b) { return new Byte(b % value);}
 		Byte opAnd(Byte b) { return new Byte(value & b.value);}
 		Byte opAnd(byte b) { return new Byte(value & b);}
 		Byte opOr(Byte b) { return new Byte(value | b.value);}
 		Byte opOr(byte b) { return new Byte(value | b);}
 		Byte opXor(Byte b) { return new Byte(value ^ b.value);}
 		Byte opXor(byte b) { return new Byte(value ^ b);}
 		Byte opShl(int i) { return new Byte(value << i);}
 		long opShl_r(long l) { return l << value;}
 		Byte opShr(int i) {return new Byte(value >> i);}
 		long opShr_r(long l) { return l >> value;}
 		Byte opUShr(int i) {return new Byte(value >>> i);}
 		long opUShr(long l) {return l >>> value;}
 		//comparison operators
 		int opEquals(Object o) {
 			if(o is Byte) {
 				Byte b = cast(Byte)o;
 				return value == b.value;
 			}
 			return o == value;
 		}
 		int opEquals(long l) {
 			return l = value;
 		}
 		int opCmp(Object o) {
 			if(o is Byte) {
 				Byte b = cast(Byte)o;
 				if(value > b.value)return 1;
 				if(value < b.value)return -1;
 				return 0;
 			}
 			return o.opCmp(value);
 		}
 		int opCmp(long l) {
 			if(value < l)return -1;
 			if(value > l)return 1;
 			return 0;
 		}
 		//assignment operators
 		Byte opAssign(byte b) {
 			value = b;
 			return this;
 		}
 		Byte opAddAssign(Byte b) {
 			value += b.value;
 			return this;
 		}
 		Byte opAddAssign(byte b) {
 			value += b;
 			return this;
 		}
 		Byte opSubAssign(Byte b) {
 			value -= b.value;
 			return this;
 		}
 		Byte opSubAssign(byte b) {
 			value -= b;
 			return this;
 		}
 		Byte opMulAssign(Byte b) {
 			value *= b.value;
 			return this;
 		}
 		Byte opMulAssign(byte b) {
 			value *= b;
 			return this;
 		}
 		Byte opDivAssign(Byte b) {
 			value /= b.value;
 			return this;
 		}
 		Byte opDivAssign(byte b) {
 			value /= b;
 			return this;
 		}
 		Byte opModAssign(Byte b) {
 			value %= b.value;
 			return this;
 		}
 		Byte opModAssign(byte b) {
 			value %= b;
 			return this;
 		}
 		Byte opAndAssign(Byte b) {
 			value &= b.value;
 			return this;
 		}
 		Byte opAndAssign(byte b) {
 			value &= b;
 			return this;
 		}
 		Byte opOrAssign(Byte b) {
 			value |= b.value;
 			return this;
 		}
 		Byte opOrAssign(byte b) {
 			value |= b;
 			return this;
 		}
 		Byte opXorAssign(Byte b) {
 			value ^= b.value;
 			return this;
 		}
 		Byte opXorAssign(byte b) {
 			value ^= b;
 			return this;
 		}
 		Byte opShlAssign(Byte b) {
 			value <<= b.value;
 			return this;
 		}
 		Byte opShlAssign(byte b) {
 			value <<= b;
 			return this;
 		}
 		Byte opShrAssign(Byte b) {
 			value >>= b.value;
 			return this;
 		}
 		Byte opShrAssign(byte b) {
 			value >>= b;
 			return this;
 		}
 		Byte opUShrAssign(Byte b) {
 			value >>>= b.value;
 			return this;
 		}
 		Byte opUShrAssign(byte b) {
 			value >>>= b;
 			return this;
 		}
 
 		// unary operators TODO add documentation
 		Byte opNeg() {return new Byte(-value);}
 		Byte opPos() {return new Byte(+value);}
 		Byte opCom() {return new Bye(~value);}
 		Byte opPostInc() {
 			scope temp = value;
 			value++;
 			return new Byte(temp);
 		}
 		Byte opPostDec() {
 			scope temp = value;
 			value--;
 			return new Byte(temp);
 		}
 }
 
 now the problem is a lack of implicit casting for user defined types the Byte
cannot be implicitly cast to byte, int, long etc. even though such a cast would
make sense.  If implicit casting were allowed then a library could be fashioned
with wrapper for all of the basic types and some less basic types such as cint,
iint, fraction, radical, ifraction, cfraction, etc.  
 
 my suggested syntax for overloading implicit casts is:
 
 implicit type1 cast(type2 var);
 
 where type1 is the type casting to and type2 is the type casting from
 this syntax would allow non-member functions to be used, so that implicit
casts can be overloaded in specific contexts 
 example:
 implicit int cast(long var){ return cast(int)var;}
 when you know that longs will never exceed the bound of ints
Jul 27 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"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...
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. How would non-member operator overloads solve this problem anyway?
Jul 28 2007
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Ender KaShae wrote:
 ...
 now the problem is a lack of implicit casting for user defined types the Byte
cannot be implicitly cast to byte, int, long etc. even though such a cast would
make sense.  If implicit casting were allowed then a library could be fashioned
with wrapper for all of the basic types and some less basic types such as cint,
iint, fraction, radical, ifraction, cfraction, etc.  
 
 my suggested syntax for overloading implicit casts is:
 
 implicit type1 cast(type2 var);
 where type1 is the type casting to and type2 is the type casting from
Eww, ick. First of all, you're overloading on return type, *and* you've introduced a new keyword. Sorry :) D already allows you to define an explicit casting overload like so: class Foo { type1 opCast() { ... } } What has been suggested a few times is to make a new operator overload that passes the result out using an out argument instead, which you *can* overload on: class Foo { void opImplicitCast(out type1) { ... } } On the issue of using a global function (as opposed to a member function), I'd rather get general type extensions, which would also provide this as a sort of side effect: bool isFoo(Object this) { return (cast(Foo)this) !is null; } auto bar = (new Foo).isFoo;
 this syntax would allow non-member functions to be used, so that implicit
casts can be overloaded in specific contexts 
 example:
 implicit int cast(long var){ return cast(int)var;}
 when you know that longs will never exceed the bound of ints
Ok, there's something very wrong about this. Altering how casts happen in particular contexts just strikes me as a very bad idea. The problem is that someone reading your code will need to go over it with a fine tooth comb to work out what casts are *actually* doing at any given line. That said, I *would* like to see implicit casts added to the language. -- Daniel
Jul 27 2007
parent Ender KaShae <astrothayne gmail.com> writes:
Daniel Keep Wrote:

 
uggested syntax for overloading implicit casts is:
 
 implicit type1 cast(type2 var);
 where type1 is the type casting to and type2 is the type casting from
Eww, ick. First of all, you're overloading on return type, *and* you've introduced a new keyword. Sorry :) D already allows you to define an explicit casting overload like so: class Foo { type1 opCast() { ... } } What has been suggested a few times is to make a new operator overload that passes the result out using an out argument instead, which you *can* overload on: class Foo { void opImplicitCast(out type1) { ... } }
I like that much better than my suggestion, it's a lot cleaner
 On the issue of using a global function (as opposed to a member
 function), I'd rather get general type extensions, which would also
 provide this as a sort of side effect:
 
 bool isFoo(Object this)
 {
     return (cast(Foo)this) !is null;
 }
 
 auto bar = (new Foo).isFoo;
 
That would be VERY useful
 
 That said, I *would* like to see implicit casts added to the language.
 
 	-- Daniel
Jul 28 2007
prev sibling next sibling parent 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...
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 operator overloads are useful for the same reason that 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 operator overloads are useful for the same reason that 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
prev sibling next sibling parent reply Ender KaShae <astrothayne gmail.com> writes:
Tristam MacDonald Wrote:

 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.
 
it actually suprised me that D doesn't allow specialised templates the way c++ does
Aug 01 2007
next sibling parent Tristam MacDonald <swiftcoder gmail.com> writes:
The problem being that I can figure otu quite *what* C++ will do in a
specialisation. Sometimes the specialisation will correctly inherit all the
original members and sometimes not, sometimes I can replace members and
sometimes not, so most of the time I must completely re-write the entire
template for each specialisation (as stdc++ does in most cases).

Ender KaShae Wrote:
 Tristam MacDonald Wrote:
 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.
 
it actually suprised me that D doesn't allow specialised templates the way c++ does
Aug 01 2007
prev sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Ender KaShae" <astrothayne gmail.com> wrote in message 
news:f8qidi$kq1$1 digitalmars.com...
 Tristam MacDonald Wrote:

 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.
it actually suprised me that D doesn't allow specialised templates the way c++ does
Ender, Please use a newsreader to read and reply to topics on these newsgroups. There is a bug in the web interface that messes up threading, and it makes all your topics very hard to follow.
Aug 01 2007
parent reply Tristam MacDonald <swiftcoder gmail.com> writes:
The I should probably assume that I am causing the same problems?
(I also use the web interface)

I don't *see* anything wrong with the threading, but I may not know what to
look for.

Jarrett Billingsley Wrote:
 "Ender KaShae" <astrothayne gmail.com> wrote in message 
 news:f8qidi$kq1$1 digitalmars.com...
 Tristam MacDonald Wrote:

 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.
it actually suprised me that D doesn't allow specialised templates the way c++ does
Ender, Please use a newsreader to read and reply to topics on these newsgroups. There is a bug in the web interface that messes up threading, and it makes all your topics very hard to follow.
Aug 01 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Tristam MacDonald" <swiftcoder gmail.com> wrote in message 
news:f8qn7h$sf7$1 digitalmars.com...
 The I should probably assume that I am causing the same problems?
 (I also use the web interface)

 I don't *see* anything wrong with the threading, but I may not know what 
 to look for.
Sometimes it happens, sometimes it doesn't. Ender's reply to Tristam (this reply) ended up not as a reply to Tristam but as a reply to the OP. Your reply, though, showed up in the right place.
Aug 01 2007
prev sibling parent Ender KaShae <astrothayne gmail.com> writes:
Jarrett Billingsley Wrote:


 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? 
 
 
lets say you have an immutable fraction class, with D the way it is you can't overload any op=, =, ++, or --, and maintain immutability with nonmember/static operator overloads you can as such: static Fraction opAddAssign(ref Fraction first, Fraction second){...} where first is assigned a new instance of Fraction
Aug 02 2007