[Home]
[Search]
[D]
TemplateDeclaration:
template TemplateIdentifier ( TemplateParameterList )
{ DeclDefs }
TemplateIdentifier:
Identifier
TemplateParameterList
TemplateParameter
TemplateParameter , TemplateParameterList
TemplateParameter:
TypeParameter
ValueParameter
AliasParameter
TemplateTypeParameter:
Identifier
Identifier TemplateTypeParameterSpecialization
Identifier TemplateTypeParameterDefault
Identifier TemplateTypeParameterSpecialization TemplateTypeParameterDefault
TemplateTypeParameterSpecialization:
: Type
TemplateTypeParameterDefault:
= Type
TemplateValueParameter:
Declaration
Declaration TemplateValueParameterSpecialization
Declaration TemplateValueParameterDefault
Declaration TemplateValueParameterSpecialization TemplateValueParameterDefault
TemplateValueParameterSpecialization:
: ConditionalExpression
TemplateValueParameterDefault:
= ConditionalExpression
TemplateAliasParameter:
alias Identifier
alias Identifier TemplateAliasParameterDefault
TemplateAliasParameterDefault:
= Type
The body of the TemplateDeclaration must be syntactically correct
even if never instantiated. Semantic analysis is not done until
instantiated. A template forms its own scope, and the template
body can contain classes, structs, types, enums, variables,
functions, and other templates.
Template parameters can be types, values, or symbols. Types can be any type. Value parameters must be of an integral type, and specializations for them must resolve to an integral constant. Symbols can be any non-local symbol.
Template parameter specializations constrain the values or types the TemplateParameter can accept.
Template parameter defaults are the value or type to use for the TemplateParameter in case one is not supplied.
TemplateInstance: TemplateIdentifer !( TemplateArgumentList ) TemplateArgumentList: TemplateArgument TemplateArgument , TemplateArgumentList TemplateArgument: Type AssignExpression SymbolOnce instantiated, the declarations inside the template, called the template members, are in the scope of the TemplateInstance:
template TFoo(T) { alias T* t; }
...
TFoo!(int).t x; // declare x to be of type int*
A template instantiation can be aliased:
template TFoo(T) { alias T* t; }
alias TFoo!(int) abc;
abc.t x; // declare x to be of type int*
Multiple instantiations of a TemplateDeclaration with the same
TemplateParameterList all will refer to the same instantiation.
For example:
template TFoo(T) { T f; }
alias TFoo(int) a;
alias TFoo(int) b;
...
a.f = 3;
assert(b.f == 3); // a and b refer to the same instance of TFoo
This is true even if the TemplateInstances are done in
different modules.
If multiple templates with the same TemplateIdentifier are declared, they are distinct if they have a different number of arguments or are differently specialized.
For example, a simple generic copy template would be:
template TCopy(T)
{
void copy(out T to, T from)
{
to = from;
}
}
To use the template, it must first be instantiated with a specific
type:
int i; TCopy!(int).copy(i, 3);
For example:
-------- module a ---------
template TFoo(T) { void bar() { func(); } }
-------- module b ---------
import a;
void func() { }
alias TFoo!(int) f; // error: func not defined in module a
and:
-------- module a ---------
template TFoo(T) { void bar() { func(1); } }
void func(double d) { }
-------- module b ---------
import a;
void func(int i) { }
alias TFoo!(int) f;
...
f.bar(); // will call a.func(double)
TemplateParameter specializations and default
values are evaluated in the scope of the TemplateDeclaration.
For each template parameter, the following rules are applied in order until a type is deduced for each parameter:
template TFoo(T) { }
alias TFoo!(int) Foo1; // (1) T is deduced to be int
alias TFoo!(char*) Foo2; // (1) T is deduced to be char*
template TFoo(T : T*) { }
alias TFoo!(char*) Foo3; // (2) T is deduced to be char
template TBar(D, U : D[]) { }
alias TBar!(int, int[]) Bar1; // (2) D is deduced to be int, U is int[]
alias TBar!(char, int[]) Bar2; // (4) error, D is both char and int
template TBar(D : E*, E) { }
alias TBar!(int*, int) Bar3; // (1) E is int
// (3) D is int*
When considering matches, a class is
considered to be a match for any super classes or interfaces:
class A { }
class B : A { }
template TFoo(T : A) { }
alias TFoo!(B) Foo4; // (3) T is B
template TBar(T : U*, U : A) { }
alias TBar!(B*, B) Foo5; // (2) T is B*
// (3) U is B
template foo(U : int, int T : 10)
{
U x = T;
}
void main()
{
assert(foo!(int, 10).x == 10);
}
template TFoo(T) { ... } // #1
template TFoo(T : T[]) { ... } // #2
template TFoo(T : char) { ... } // #3
template TFoo(T,U,V) { ... } // #4
alias TFoo!(int) foo1; // instantiates #1
alias TFoo!(double[]) foo2; // instantiates #2 with T being double
alias TFoo!(char) foo3; // instantiates #3
alias TFoo!(char, int) fooe; // error, number of arguments mismatch
alias TFoo!(char, int, int) foo4; // instantiates #4
The template picked to instantiate is the one that is most specialized
that fits the types of the TemplateArgumentList.
Determine which is more specialized is done the same way as the
C++ partial ordering rules.
If the result is ambiguous, it is an error.
int x;
template Foo(alias X)
{
static int* p = &X;
}
void test()
{
alias Foo!(x) bar;
*bar.p = 3; // set x to 3
int y;
alias Foo!(y) abc; // error, y is local name
}
class Foo
{
static int p;
}
template Bar(alias T)
{
alias T.p q;
}
void test()
{
alias Bar!(Foo) bar;
bar.q = 3; // sets Foo.p to 3
}
import std.string;
template Foo(alias X)
{
alias X.toString y;
}
void test()
{
alias Foo!(std.string) bar;
bar.y(3); // calls std.string.toString(3)
}
int x;
template Foo(alias X)
{
static int* p = &X;
}
template Bar(alias T)
{
alias T!(x) abc;
}
void test()
{
alias Bar!(Foo) bar;
*bar.abc.p = 3; // sets x to 3
}
int x;
template Foo(alias X)
{
static int* p = &X;
}
template Bar(alias T)
{
alias T.p q;
}
void test()
{
alias Foo!(x) foo;
alias Bar!(foo) bar;
*bar.q = 3; // sets x to 3
}
template Foo(T, U = int) { ... }
Foo!(uint,long); // instantiate Foo with T as uint, and U as long
Foo!(uint); // instantiate Foo with T as uint, and U as int
template Foo(T, U = T*) { ... }
Foo!(uint); // instantiate Foo with T as uint, and U as uint*
template Foo(T)
{
T Foo; // declare variable Foo of type T
}
void test()
{
Foo!(int) = 6; // instead of Foo!(int).Foo
}
ClassTemplateDeclaration:
class Identifier ( TemplateParameterList ) [SuperClass {, InterfaceClass }] ClassBody
If a template declares exactly one member, and that member is a class
with the same name as the template:
template Bar(T)
{
class Bar
{
T member;
}
}
then the semantic equivalent, called a ClassTemplateDeclaration
can be written as:
class Bar(T)
{
T member;
}
template factorial(int n : 1)
{
enum { factorial = 1 }
}
template factorial(int n)
{
// Note . used to find global template rather than enum
enum { factorial = n* .factorial!(n-1) }
}
void test()
{
printf("%d\n", factorial!(4)); // prints 24
}
class Foo
{
template TBar(T)
{
T xx; // Error
int func(T) { ... } // Error
static T yy; // Ok
static int func(T t, int y) { ... } // Ok
}
}
Templates cannot be declared inside functions.