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