Time to kill T() as (sometimes) working T.init alias ?

Dmitry Olshansky dmitry.olsh at gmail.com
Sun Nov 25 08:47:01 PST 2012


There was a topic on this NG before by deadalnix but apparently failed 
to penetrate the masses.I've brought some unpleasant facts together to 
get it a death warrant.

1. Quick intro. An example of surprise factor with T() and templated 
constructor.

import std.conv;
struct A{
    int[] padded;
    this(T...)(T args){
     padded = [0];
     foreach(i, v; T)
         padded ~= to!int(args[i]);
     padded ~= 0;
    }
//...
}

unitest{
     A a = A(2, 3);
     assert(a.padded == [0, 2, 3, 0]);
     A b = A(1);
     assert(b.padded == [0, 1, 0]);
     A c = A();
     assert(c.padded is null); //Spoiler: it passes ;)
}

Enjoy the nice pitfall. Now if it was inside generic function then you'd 
better check if your type-tuple is empty because compiler will go on 
limb and substitute it with T.int:

void someFunc(T...)(T args)
{
    ...
    //somewhere we need to pass some of args to construct A
    A x = A(args[1..$]); //may or may not call constructor depending on args
}

For more real world example - it happens with Phobos containers - you 
can't create an empty one. Either T.init or non-zero argument list.
See e.g. this pull https://github.com/D-Programming-Language/phobos/pull/953

2. Let's go a bit further with generic types. Suppose we remove T() as 
T.init. What if it breaks somebody's rigorously maintained code?
Surprise - this notation doesn't even exist for built-in types (unlike 
T.init). Say with:

int fooTest(T)(){
     T test = T();
     return 3;
}

//next line fails to compile with:
//Error: function expected before (), not int of type int
static assert(fooTest!int() == 3);

static assert(fooTest!A() == 3); //OK

3. Even further any code relying on some arbitrary (but not built-in!) 
type to have T() return T.init is broken in many ways as there is static 
opCall, that instead can say... order you a burrito (depending on 
author's maliciousness) ?
Simply put there is not even a single guarantee that static opCall of 
type T will return that particular type.

After killing this ugly craft the suggestion is to, of course, introduce 
0-argument constructors. The syntax is already there. static opCall 
can't prevent introduction of 0-arg constructor as the compiler already 
has to disambiguate between the static opCall and the ctor. (BTW which 
one wins?)

It'll also help immensely people that currently use static opCall to 
emulate 0-arg ctor and thus use convention rather then guarantee that it 
does the job of 0-arg constructor.

Thoughts?

-- 
Dmitry Olshansky


More information about the Digitalmars-d mailing list