www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Implicit type conversion

reply Michal Minich <michal.minich gmail.com> writes:
Use case:

import std.variant;

void foo (Variant v) {}

void main () {
    Variant v = 3; // ok, this (....) called
    v = 3;         // ok, opAssing  called
    foo (v);       // ok, struct copy, this(this) called
    foo (3);       // error
}

I'm trying to understand what is needed to make this work from language 
design perspective. Is there already known/proposed solution to make this 
work? What comes to my mind as first - Variant constructor should be 
called on last line, the same way as on the first. But maybe to solve 
this, another operator is needed, opImplicitConvertFrom ...

Second question. Why is needed to have both - strut constructor (this) 
and opAssing. In variant case, constructor just forwards to opAssing. 
From high level point of view, I don't see any reason both should behave 
differently...
Jan 08 2011
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday 08 January 2011 13:16:54 Michal Minich wrote:
 Use case:
 
 import std.variant;
 
 void foo (Variant v) {}
 
 void main () {
     Variant v = 3; // ok, this (....) called
     v = 3;         // ok, opAssing  called
     foo (v);       // ok, struct copy, this(this) called
     foo (3);       // error
 }
 
 I'm trying to understand what is needed to make this work from language
 design perspective. Is there already known/proposed solution to make this
 work? What comes to my mind as first - Variant constructor should be
 called on last line, the same way as on the first. But maybe to solve
 this, another operator is needed, opImplicitConvertFrom ...
There are definitely folks who want opImplicitCast (I think that it's pretty much going to be necessary to get std.typecons.Rebindable to work entirely correctly), and there's at least a decent chance that it will get added, but in general, D avoids implicit casting in order to avoid issues where you call functions which you don't really want to call (see http://is.gd/ksVli ). Regardless, opImplicitCast wouldn't help you here anyway, because you need to cast from an int to a Variant rather than the other way around, and it's not like int is going to have opImplicitCast defined, even if it did exist. Generally, you just need to give the correct type, which in this case would presumably mean foo(Variant(3)). It might be a bid annoying, but it avoids problems where you end up calling a function overload that you didn't intend to. If you really don't want to do that, then I believe that you're going to have to create another function overload for Foo which takes an int and deals with the conversion to Variant for you.
 Second question. Why is needed to have both - strut constructor (this)
 and opAssing. In variant case, constructor just forwards to opAssing.
 From high level point of view, I don't see any reason both should behave
 differently...
There could be a difference of efficiency depending on the type. It could be more efficient to do something differently in one. For instance, that's why we have both opAssign and opOpAssign. Technically, you could just not have opOpAssign and use the combination of opAssign and opBinary to do it, but then you're likely to end up with extra heap allocations and the like. It could be the same with a constructor and opAssign, depending on the type. For the most part, D has tried to reduce how many operators you have to declare when they're related (such us using opCmp() to give you <, <=, >=, and >), but it does create extra ones in cases where there's a good chance that it's more efficient to have separate definitions (such as using opEquals() for == and != rather than using opCmp() for that). - Jonathan M Davis
Jan 09 2011
parent Michal Minich <michal.minich gmail.com> writes:
V Sun, 09 Jan 2011 21:29:32 -0800, Jonathan M Davis wrote:

 On Saturday 08 January 2011 13:16:54 Michal Minich wrote:
 Use case:
 
 import std.variant;
 
 void foo (Variant v) {}
 
 void main () {
     Variant v = 3; // ok, this (....) called v = 3;         // ok,
     opAssing  called foo (v);       // ok, struct copy, this(this)
     called foo (3);       // error
 }
 
 I'm trying to understand what is needed to make this work from language
 design perspective. Is there already known/proposed solution to make
 this work? What comes to my mind as first - Variant constructor should
 be called on last line, the same way as on the first. But maybe to
 solve this, another operator is needed, opImplicitConvertFrom ...
There are definitely folks who want opImplicitCast (I think that it's pretty much going to be necessary to get std.typecons.Rebindable to work entirely correctly), and there's at least a decent chance that it will get added, but in general, D avoids implicit casting in order to avoid issues where you call functions which you don't really want to call (see http://is.gd/ksVli ). Regardless, opImplicitCast wouldn't help you here anyway, because you need to cast from an int to a Variant rather than the other way around, and it's not like int is going to have opImplicitCast defined, even if it did exist. Generally, you just need to give the correct type, which in this case would presumably mean foo(Variant(3)). It might be a bid annoying, but it avoids problems where you end up calling a function overload that you didn't intend to. If you really don't want to do that, then I believe that you're going to have to create another function overload for Foo which takes an int and deals with the conversion to Variant for you.
 Second question. Why is needed to have both - strut constructor (this)
 and opAssing. In variant case, constructor just forwards to opAssing.
 From high level point of view, I don't see any reason both should
 behave differently...
There could be a difference of efficiency depending on the type. It could be more efficient to do something differently in one. For instance, that's why we have both opAssign and opOpAssign. Technically, you could just not have opOpAssign and use the combination of opAssign and opBinary to do it, but then you're likely to end up with extra heap allocations and the like. It could be the same with a constructor and opAssign, depending on the type. For the most part, D has tried to reduce how many operators you have to declare when they're related (such us using opCmp() to give you <, <=,
=, and >), but it does create extra ones in cases where there's a good
chance that it's more efficient to have separate definitions (such as using opEquals() for == and != rather than using opCmp() for that). - Jonathan M Davis
Your suggestion to create function overload and bearophile’s bug report http://d.puremagic.com/issues/show_bug.cgi?id=5368 to remove variadic functions for class objects has given me an idea: Currently this functionality works only with non-teplated class constructors. If it were extended to support template and strut constructors, then it would make possible to construct objects/struct from function arguments, and the interaction with overloading should be more clear because of required variadic annotation on function parameter. In example: struct S { this (T…) (T val) {……} } void foo (S s…) { } //<- the dots here expand the constructor parameters vod main () { foo (3); // is the same as: foo (S(3)); } But this is patchy solution. Proper way would be to make new operator function opArgConstruct, which would serve as constructor when struct is in place of function parameter. This way struct author can control availability of this idom. This function should normally just forward the call to constructor. This functionality is need usually for atomic (one valued) type wrappers like Variant, RefCounted, Rebindable... If this kind of struct constructor is constrained only to accept one argument, it would make overloading matter less, and ‘this’ can be used for construction.
Jan 10 2011