I have a struct Foo!T
and a function that operates on any two Foo!T
.
I would expect such a function to be declared
void fun(U)(U a, U b) if (is(U : Foo!T, T...)) { }
However, it turns out that I can declare it as
void fun(U)(U a, U b) if (is(U : Foo)) { }
only if I declare it inside the body of Foo
.
For example:
struct Foo(T) {
void fun1(U)(U b) if (is(U : Foo)) { }
}
void fun2(U)(U a, U b) if (is(U : Foo)) { }
unittest {
Foo!int f;
f.fun1(f);
f.fun2(f);
}
The above fails with struct d.Foo(T) is used as a type
on the invocation of
fun2
. fun1
, however, is fine.
Why is the constraint is(U : Foo)
valid inside the body of Foo
, but not
outside?
Is the comparison is(U : Foo)
inside the body of Foo
equivalent to is(U :
Foo!V, V...)
outside the body of Foo
?
Inside of a templated type, the name of the template refers to that particular instantiation of the template except when it's explicitly given a different instantiation. For instance,
struct Foo(T)
{
pragma(msg, Foo.stringof);
}
pragma(msg, Foo.stringof);
void main()
{
Foo!int foo1;
Foo!string foo2;
}
prints
Foo(T)
Foo!int
Foo!string
The first pragma printed is the one right after the template, and the other two are generated when Foo
is instantiated. This makes it so that you don't have to plaster Foo!T
all over the place inside your struct declaration when you're referring to it (e.g. when returning it from a member function). This is particularly useful if the type has several template parameters instead of just one. But it does mean that if you want to refer to the general template, you either need to instantiate it with specific arguments - e.g. use Foo!int
inside of Foo
to refer to Foo!int
regardless of what the current instantiaton is - or you need to use the dot operator to indicate that you want Foo
from the outer scope. e.g.
struct Foo(T)
{
pragma(msg, Foo.stringof);
pragma(msg, .Foo.stringof);
}
pragma(msg, Foo.stringof);
void main()
{
Foo!int foo1;
Foo!string foo2;
}
prints
Foo(T)
Foo!int
Foo(T)
Foo!string
Foo(T)
So, when writing stuff like template constraints inside of a templated type, be aware that using Foo
inside of struct Foo(T)
or class Foo(T)
will mean that specific instantiation and not the template itself.
Also, if you're looking specifically to test whether U
is an instantition of Foo
, I'd suggest using std.traits.isInstanceOf
- e.g. if(isInstanceOf(Foo, U))
. Arguably, it really should be isInstantiationOf
rather than isInstanceOf
, but regardless, it does the job in a more idiomatic way than using naked is
expressions.