Operator Overloading and boilerplate code

Ali Çehreli acehreli at yahoo.com
Tue Jul 5 09:05:13 PDT 2011


On Tue, 05 Jul 2011 16:20:44 +0200, Loopback wrote:

> On 2011-07-05 03:11, Ali Çehreli wrote:

>> struct S
>> {
>>      this(T)(double d)
>>      {}
>> }
>>
>> void main()
>> {
>>      auto o = S(1.5);
>> }
>>
>> Error: template deneme.S.__ctor(T) does not match any function template
>> declaration
>> Error: template deneme.S.__ctor(T) cannot deduce template function from
>> argument types !()(double)
>>
>> The compiler is right: What should T be there? int? string? MyClass?
>>
>> I've realized again that I don't know how to specify the template
>> parameter for the constructor. The following attempt fails as the
>> compiler thinks S itself is a template:
>>
>>      auto o = S!string(1.5);
>>
>> Error: template instance S is not a template declaration, it is a
>> struct
>>
>> And if I try to be smart after the error message, this seg faults the
>> compiler:
>>
>>      auto o = S.__ctor!string(1.5);
>>
>> Ali
> Hmm... Interesting. Thank you for clarifying and explaining that!
> 
> I guess supplying T to the constructor when the parameters are already
> known to avoid compiler errors is not a solution then. Seems to me as if
> its only aggravates things.
> 
> Though is there no solution nor any workarounds for this problem? I've
> attempted to use two different types of constructors and both appeared
> to be very limited, and I do not believe that this is the case.

I don't want to look like brushing off the problem but having many 
constructors make the code complicated. For example, it may be confusing 
which constructor gets called here:

    auto d = DVECTOR2(1.5);

Are we setting just x or both x and y? Especially when we know that 
DVECTOR2 is a struct and that structs have this feature of assigning 
default values to the trailing unspecified members (at least in some 
cases), I think a factory function would be better in this case:

    auto d = square_DVECTOR2(1.5);

Now we know that both x and y will be 1.5.

Same can be said for some of the other constructors. It is not difficult 
at all for the caller to give us what we want; and it is clearer:

    D3DXVECTOR2 d3;
    // ...
    auto d = DVECTOR2(d3.tupleof);

(I think this is in line with Kevlin Henney's "Parameterize from Above" 
guideline/pattern/idiom/etc. :))

> If you use a generic constructor is there no possible way to use it in
> cases where immutable and const is involved? Or is there a page that I
> have missed perhaps?

D2 has changed the meaning of inout to mean something like "templatize 
just the mutable/const/immutable qualification of the parameter" but it 
is not implemented fully yet. Look at "Inout Functions" on the Functions 
spec:

  http://d-programming-language.org/function.html

> struct DVECTOR2
> {
> 	// Controls that the parameter is a valid type template Accepts
(T) {
> 	enum Accepts = is(T == DVECTOR2) || is(T == float) || is(T ==
> 	D3DXVECTOR2) || is(T == POINT); }
> 	
> 	// Whether the parameter is a float or not
>  	template isScalar(T) { enum isScalar = is(T == float); }
> 	
> 	// The Variables
> 	float x = 0f;
> 	float y = 0f;
> 
> 	// Default Constructor
> 	this()(float x, float y)
> 	{
> 		this.x = x;
> 		this.y = y;
> 	}
> 
> 	// Float Constructor
>  	this()(float xy) { this(xy, xy); }
> 
> 	// Implement D3DXVECTOR2 and POINT support
>       this(T)(T arg) if (Accepts!T
> 	&& !isScalar!T) { this(arg.tupleof); }

Ali


More information about the Digitalmars-d-learn mailing list