$(DDOC $(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(SPEC_S Templates,
$(DDOC_BLANKLINE )
$(HEADERNAV_TOC $(HEADERNAV_ITEM declarations, Template Declarations)
$(HEADERNAV_SUBITEMS template_instantiation, Template Instantiation,
$(HEADERNAV_ITEM explicit_tmp_instantiation, Explicit Template Instantiation)
$(HEADERNAV_ITEM common_instantiation, Common Instantiation)
$(HEADERNAV_ITEM copy_example, Practical Example)
$(HEADERNAV_ITEM instantiation_scope, Instantiation Scope)
)
$(HEADERNAV_SUBITEMS parameters, Template Parameters,
$(HEADERNAV_ITEM template_type_parameters, Type Parameters)
$(HEADERNAV_ITEM template_this_parameter, This Parameters)
$(HEADERNAV_ITEM template_value_parameter, Value Parameters)
$(HEADERNAV_ITEM aliasparameters, Alias Parameters)
$(HEADERNAV_ITEM variadic-templates, Sequence Parameters)
$(HEADERNAV_ITEM template_parameter_def_values, Default Arguments)
)
$(HEADERNAV_ITEM implicit_template_properties, Eponymous Templates)
$(HEADERNAV_ITEM aggregate_templates, Aggregate Type Templates)
$(HEADERNAV_SUBITEMS function-templates, Function Templates,
$(HEADERNAV_ITEM ifti, Implicit Function Template Instantiation (IFTI))
$(HEADERNAV_ITEM return-deduction, Return Type Deduction)
$(HEADERNAV_ITEM auto-ref-parameters, Auto Ref Parameters)
$(HEADERNAV_ITEM function-default, Default Arguments)
)
$(HEADERNAV_ITEM template_ctors, Template Constructors)
$(HEADERNAV_ITEM variable-template, Enum & Variable Templates)
$(HEADERNAV_ITEM alias-template, Alias Templates)
$(HEADERNAV_SUBITEMS nested-templates, Nested Templates,
$(HEADERNAV_ITEM limitations, Aggregate Type Limitations)
$(HEADERNAV_ITEM implicit-nesting, Implicit Nesting)
$(HEADERNAV_ITEM nested_template_limitation, Context Limitation)
)
$(HEADERNAV_ITEM recursive_templates, Recursive Templates)
$(HEADERNAV_ITEM template_constraints, Template Constraints)
)
$(DDOC_BLANKLINE )
$(LNAME2 declarations, Template Declarations)
$(DDOC_BLANKLINE )
$(P Templates are D's approach to generic programming.
Templates can be defined with a $(I TemplateDeclaration):
)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME TemplateDeclaration):
$(D template) $(GLINK_LEX Identifier) $(GLINK TemplateParameters) $(GLINK Constraint)$(OPT ) $(D {) $(GLINK2 module, DeclDefs)$(OPT ) $(D })
$(DDOC_BLANKLINE )
$(GNAME TemplateParameters):
$(D $(LPAREN)) $(GLINK TemplateParameterList)$(OPT ) $(D $(RPAREN ))
$(DDOC_BLANKLINE )
$(GNAME TemplateParameterList):
$(GLINK TemplateParameter)
$(GLINK TemplateParameter) $(D ,)
$(GLINK TemplateParameter) $(D ,) $(GSELF TemplateParameterList)
)
$(DDOC_BLANKLINE )
$(P The DeclDefs body of the template must be syntactically correct
even if never instantiated. Semantic analysis is not done until
instantiation. A template forms its own scope, and the template
body can contain declarations such as classes, structs, types,
enums, variables, functions, and other templates.
)
$(DDOC_BLANKLINE )
$(P $(RELATIVE_LINK2 parameters, Template parameters) can take types,
values, symbols, or sequences.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) t(T) $(D_COMMENT // declare type parameter T
){
T v; $(D_COMMENT // declare a member variable of type T within template t
)}
)
$(DDOC_BLANKLINE )
$(P A template parameter can have a specialization which
constrains an argument the $(I TemplateParameter) can
accept.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) t(T : $(D_KEYWORD int)) $(D_COMMENT // type T must implicitly convert to int
){
...
}
)
$(DDOC_BLANKLINE )
$(P If multiple templates with the same $(I Identifier) are
declared, they are distinct if they have different parameters
or are differently specialized.
)
$(DDOC_BLANKLINE )
$(P If a template has a member which has the same identifier as the
template, the template is an
$(RELATIVE_LINK2 implicit_template_properties, Eponymous Template).
template
declarations with one eponymous member are usually
written as specific $(RELATIVE_LINK2 aggregate_templates, short syntax)
template declarations instead.)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(LNAME2 template_instantiation, Template Instantiation)
$(DDOC_BLANKLINE )
$(P A template must be instantiated before use. This means
passing an argument list to the template.
Those arguments are typically then substituted
into the template body, which becomes a new scoped entity.)
$(DDOC_BLANKLINE )
$(P $(RELATIVE_LINK2 function-templates, Function templates) can
be implicitly instantiated if the compiler can infer the template
arguments from a function call. Otherwise the template must be
instantiated explicitly.)
$(DDOC_BLANKLINE )
$(LNAME2 explicit_tmp_instantiation, Explicit Template Instantiation)
$(DDOC_BLANKLINE )
$(P Templates are explicitly instantiated with:
)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME TemplateInstance):
$(GLINK_LEX Identifier) $(GLINK TemplateArguments)
$(DDOC_BLANKLINE )
$(GNAME TemplateArguments):
$(D ! $(LPAREN)) $(GLINK TemplateArgumentList)$(OPT ) $(D $(RPAREN ))
$(D !) $(GLINK TemplateSingleArgument)
$(DDOC_BLANKLINE )
$(GNAME TemplateArgumentList):
$(GLINK TemplateArgument)
$(GLINK TemplateArgument) $(D ,)
$(GLINK TemplateArgument) $(D ,) $(GSELF TemplateArgumentList)
$(DDOC_BLANKLINE )
$(GNAME TemplateArgument):
$(GLINK2 type, Type)
$(ASSIGNEXPRESSION )
$(GLINK Symbol)
$(DDOC_BLANKLINE )
$(GNAME Symbol):
$(GLINK SymbolTail)
$(D .) $(GLINK SymbolTail)
$(DDOC_BLANKLINE )
$(GNAME SymbolTail):
$(GLINK_LEX Identifier)
$(GLINK_LEX Identifier) $(D .) $(GSELF SymbolTail)
$(GLINK TemplateInstance)
$(GLINK TemplateInstance) $(D .) $(GSELF SymbolTail)
$(DDOC_BLANKLINE )
$(GNAME TemplateSingleArgument):
$(GLINK_LEX Identifier)
$(GLINK2 type, FundamentalType)
$(GLINK_LEX CharacterLiteral)
$(GLINK_LEX StringLiteral)
$(GLINK_LEX IntegerLiteral)
$(GLINK_LEX FloatLiteral)
$(D true)
$(D false)
$(D null)
$(D this)
$(GLINK2 expression, SpecialKeyword)
)
$(DDOC_BLANKLINE )
$(P Once instantiated, the declarations inside the template, called
the template members, are in the scope
of the $(I TemplateInstance):)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) TFoo(T) { $(D_KEYWORD alias) Ptr = T*; }
...
TFoo!($(D_KEYWORD int)).Ptr x; $(D_COMMENT // declare x to be of type int*
))
$(DDOC_BLANKLINE )
$(P If the $(GLINK TemplateArgument) is one token long, the parentheses can be omitted:)
$(DDOC_BLANKLINE )
$(D_CODE TFoo!$(D_KEYWORD int).Ptr x; $(D_COMMENT // same as TFoo!$(LPAREN)int$(RPAREN ).Ptr x;
))
$(DDOC_BLANKLINE )
$(P A template instantiation can be aliased:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) TFoo(T) { $(D_KEYWORD alias) Ptr = T*; }
$(D_KEYWORD alias) foo = TFoo!($(D_KEYWORD int));
foo.Ptr x; $(D_COMMENT // declare x to be of type int*
))
$(DDOC_BLANKLINE )
$(LNAME2 common_instantiation, Common Instantiation)
$(DDOC_BLANKLINE )
$(P Multiple instantiations of a $(I TemplateDeclaration) with the same
$(I TemplateArgumentList) will all refer to the same instantiation.
For example:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) TFoo(T) { T f; }
$(D_KEYWORD alias) a = TFoo!($(D_KEYWORD int));
$(D_KEYWORD alias) b = TFoo!($(D_KEYWORD int));
...
a.f = 3;
$(D_KEYWORD assert)(b.f == 3); $(D_COMMENT // a and b refer to the same instance of TFoo
))
$(DDOC_BLANKLINE )
$(P This is true even if the $(I TemplateInstance)s are done in
different modules.
)
$(DDOC_BLANKLINE )
$(P Even if template arguments are implicitly converted to the same
template parameter type, they still refer to the same instance.
This example uses a $(GLINK TemplateValueParameter) and a
$(RELATIVE_LINK2 aggregate_templates, struct
template):)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD struct) TFoo($(D_KEYWORD int) x) { }
$(D_COMMENT // Different template parameters create different struct types
)$(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD is)(TFoo!(3) == TFoo!(2)));
$(D_COMMENT // 3 and 2+1 are both 3 of type int - same TFoo instance
)$(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD is)(TFoo!(3) == TFoo!(2 + 1)));
$(D_COMMENT // 3u is implicitly converted to 3 to match int parameter,
)$(D_COMMENT // and refers to exactly the same instance as TFoo!$(LPAREN)3$(RPAREN )
)$(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD is)(TFoo!(3) == TFoo!(3u)));
)
$(DDOC_BLANKLINE )
$(LNAME2 copy_example, Practical Example)
$(DDOC_BLANKLINE )
$(P A simple generic copy template would be:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) TCopy(T)
{
$(D_KEYWORD void) copy($(D_KEYWORD out) T to, T from)
{
to = from;
}
}
)
$(DDOC_BLANKLINE )
$(P To use this template, it must first be instantiated with a specific
type:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD int) i;
TCopy!($(D_KEYWORD int)).copy(i, 3);
)
$(P See also $(RELATIVE_LINK2 function-templates, function templates).)
$(DDOC_BLANKLINE )
$(LNAME2 instantiation_scope, Instantiation Scope)
$(DDOC_BLANKLINE )
$(P $(I TemplateInstance)s are always instantiated in the scope of where
the $(I TemplateDeclaration) is declared, with the addition of the
template parameters being declared as aliases for their deduced types.
)
$(P $(B Example:))
$(DDOC_BLANKLINE )
$(U module a:)
$(D_CODE $(D_KEYWORD template) TFoo(T) { $(D_KEYWORD void) bar() { func(); } }
)
$(DDOC_BLANKLINE )
$(U module b:)
$(D_CODE $(D_KEYWORD import) a;
$(D_KEYWORD void) func() { }
$(D_KEYWORD alias) f = TFoo!($(D_KEYWORD int)); $(D_COMMENT // error: func not defined in module a
))
$(DDOC_BLANKLINE )
$(P $(B Example:))
$(DDOC_BLANKLINE )
$(U module a:)
$(D_CODE $(D_KEYWORD template) TFoo(T) { $(D_KEYWORD void) bar() { func(1); } }
$(D_KEYWORD void) func($(D_KEYWORD double) d) { }
)
$(DDOC_BLANKLINE )
$(U module b:)
$(D_CODE $(D_KEYWORD import) a;
$(D_KEYWORD void) func($(D_KEYWORD int) i) { }
$(D_KEYWORD alias) f = TFoo!($(D_KEYWORD int));
...
f.bar(); $(D_COMMENT // will call a.func$(LPAREN)double$(RPAREN )
))
$(DDOC_BLANKLINE )
$(P $(I TemplateParameter) specializations and default
arguments are evaluated in the scope of the $(I TemplateDeclaration).
)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(LNAME2 parameters, Template Parameters)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME TemplateParameter):
$(GLINK TemplateTypeParameter)
$(GLINK TemplateValueParameter)
$(GLINK TemplateAliasParameter)
$(GLINK TemplateSequenceParameter)
$(GLINK TemplateThisParameter)
)
$(DDOC_BLANKLINE )
$(P Template parameters can take types, values, symbols, or sequences.)
$(DDOC_BLANKLINE )
$(UL $(LI Type parameters can take any type.)
$(LI Value parameters can take any expression which can be statically
evaluated at compile time.)
$(LI Alias parameters can take almost any symbol.)
$(LI Sequence parameters can take zero or more types, values or symbols.)
)
$(DDOC_BLANKLINE )
$(P $(RELATIVE_LINK2 template_parameter_def_values, A default argument)
specifies the type, value or symbol to use for the
$(I TemplateParameter) when a matching argument is not supplied.
)
$(DDOC_BLANKLINE )
$(LNAME2 template_type_parameters, Type Parameters)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME TemplateTypeParameter):
$(GLINK_LEX Identifier)
$(GLINK_LEX Identifier) $(GLINK TemplateTypeParameterSpecialization)
$(GLINK_LEX Identifier) $(GLINK TemplateTypeParameterDefault)
$(GLINK_LEX Identifier) $(GLINK TemplateTypeParameterSpecialization) $(GLINK TemplateTypeParameterDefault)
$(DDOC_BLANKLINE )
$(GNAME TemplateTypeParameterSpecialization):
$(D :) $(GLINK2 type, Type)
$(DDOC_BLANKLINE )
$(GNAME TemplateTypeParameterDefault):
$(D =) $(GLINK2 type, Type)
)
$(DDOC_BLANKLINE )
$(LNAME2 parameters_specialization, Specialization and Pattern Matching)
$(DDOC_BLANKLINE )
$(P Templates may be specialized for particular types of arguments
by following the template parameter identifier with a :
and the
pattern for the specialized type.
For example:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) TFoo(T) { ... } $(D_COMMENT // #1
)$(D_KEYWORD template) TFoo(T : T[]) { ... } $(D_COMMENT // #2
)$(D_KEYWORD template) TFoo(T : $(D_KEYWORD char)) { ... } $(D_COMMENT // #3
)$(D_KEYWORD template) TFoo(T, U, V) { ... } $(D_COMMENT // #4
)
$(D_KEYWORD alias) foo1 = TFoo!($(D_KEYWORD int)); $(D_COMMENT // instantiates #1
)$(D_KEYWORD alias) foo2 = TFoo!($(D_KEYWORD double)[]); $(D_COMMENT // instantiates #2 matching pattern T[] with T being double
)$(D_KEYWORD alias) foo3 = TFoo!($(D_KEYWORD char)); $(D_COMMENT // instantiates #3
)$(D_KEYWORD alias) fooe = TFoo!($(D_KEYWORD char), $(D_KEYWORD int)); $(D_COMMENT // error, number of arguments mismatch
)$(D_KEYWORD alias) foo4 = TFoo!($(D_KEYWORD char), $(D_KEYWORD int), $(D_KEYWORD int)); $(D_COMMENT // instantiates #4
))
$(DDOC_BLANKLINE )
$(P The template picked to instantiate is the one that is most specialized
that fits the types of the $(I TemplateArgumentList).
If the result is ambiguous, it is an error.
)
$(DDOC_BLANKLINE )
$(LNAME2 argument_deduction, Type Parameter Deduction)
$(DDOC_BLANKLINE )
$(P The types of template parameters are deduced for a particular
template instantiation by comparing the template argument with
the corresponding template parameter.
)
$(DDOC_BLANKLINE )
$(P For each template parameter, the following rules are applied in
order until a type is deduced for each parameter:
)
$(DDOC_BLANKLINE )
$(OL $(LI If there is no type specialization for the parameter,
the type of the parameter is set to the template argument.)
$(DDOC_BLANKLINE )
$(LI If the type specialization is dependent on a type parameter,
the type of that parameter is set to be the corresponding part
of the type argument.)
$(DDOC_BLANKLINE )
$(LI If after all the type arguments are examined, there are any
type parameters left with no type assigned, they are assigned
types corresponding to the template argument in the same position
in the $(I TemplateArgumentList).)
$(DDOC_BLANKLINE )
$(LI If applying the above rules does not result in exactly one
type for each template parameter, then it is an error.)
)
$(DDOC_BLANKLINE )
$(P For example:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) TFoo(T) { }
$(D_KEYWORD alias) foo1 = TFoo!($(D_KEYWORD int)); $(D_COMMENT // $(LPAREN)1$(RPAREN ) T is deduced to be int
)$(D_KEYWORD alias) foo2 = TFoo!($(D_KEYWORD char)*); $(D_COMMENT // $(LPAREN)1$(RPAREN ) T is deduced to be char*
)
$(D_KEYWORD template) TBar(T : T*) { } $(D_COMMENT // match template argument against T* pattern
)$(D_KEYWORD alias) bar = TBar!($(D_KEYWORD char)*); $(D_COMMENT // $(LPAREN)2$(RPAREN ) T is deduced to be char
)
$(D_KEYWORD template) TAbc(D, U : D[]) { } $(D_COMMENT // D[] is pattern to be matched
)$(D_KEYWORD alias) abc1 = TAbc!($(D_KEYWORD int), $(D_KEYWORD int)[]); $(D_COMMENT // $(LPAREN)2$(RPAREN ) D is deduced to be int, U is int[]
)$(D_KEYWORD alias) abc2 = TAbc!($(D_KEYWORD char), $(D_KEYWORD int)[]); $(D_COMMENT // $(LPAREN)4$(RPAREN ) error, D is both char and int
)
$(D_KEYWORD template) TDef(D : E*, E) { } $(D_COMMENT // E* is pattern to be matched
)$(D_KEYWORD alias) def = TDef!($(D_KEYWORD int)*, $(D_KEYWORD int)); $(D_COMMENT // $(LPAREN)1$(RPAREN ) E is int
) $(D_COMMENT // $(LPAREN)3$(RPAREN ) D is int*
))
$(DDOC_BLANKLINE )
$(P Deduction from a specialization can provide values
for more than one parameter:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) Foo(T: T[U], U)
{
...
}
Foo!($(D_KEYWORD int)[$(D_KEYWORD long)]) $(D_COMMENT // instantiates Foo with T set to int, U set to long
))
$(DDOC_BLANKLINE )
$(P When considering matches, a class is
considered to be a match for any super classes or interfaces:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD class) A { }
$(D_KEYWORD class) B : A { }
$(D_KEYWORD template) TFoo(T : A) { }
$(D_KEYWORD alias) foo = TFoo!(B); $(D_COMMENT // $(LPAREN)3$(RPAREN ) T is B
)
$(D_KEYWORD template) TBar(T : U*, U : A) { }
$(D_KEYWORD alias) bar = TBar!(B*, B); $(D_COMMENT // $(LPAREN)2$(RPAREN ) T is B*
) $(D_COMMENT // $(LPAREN)3$(RPAREN ) U is B
))
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
$(LNAME2 template_this_parameter, This Parameters)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME TemplateThisParameter):
$(D this) $(GLINK TemplateTypeParameter)
)
$(DDOC_BLANKLINE )
$(P $(I TemplateThisParameter)s are used in member function templates
to pick up the type of the $(I this) reference. It also will
infer the mutability of the this
reference. For example, if
this
is const
, then the function is marked const
.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S
{
$(D_KEYWORD void) foo($(D_KEYWORD this) T)() $(D_KEYWORD const)
{
$(D_KEYWORD pragma)(msg, T);
}
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD const)(S) s;
(&s).foo();
S s2;
s2.foo();
$(D_KEYWORD immutable)(S) s3;
s3.foo();
}
)
)
$(DDOC_BLANKLINE )
Prints:
$(DDOC_BLANKLINE )
$(CONSOLE const(S)
S
immutable(S)
)
$(DDOC_BLANKLINE )
$(LNAME2 this_rtti, Avoiding Runtime Type Checks)
$(DDOC_BLANKLINE )
$(P TemplateThisParameter is especially useful when used with inheritance. For example,
consider the implementation of a final base method which returns a derived
class type. Typically this would return a base type, but that would prohibit
calling or accessing derived properties of the type:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD interface) Addable(T)
{
$(D_KEYWORD final) $(D_KEYWORD auto) add(T t)
{
$(D_KEYWORD return) $(D_KEYWORD this);
}
}
$(D_KEYWORD class) List(T) : Addable!T
{
List remove(T t)
{
$(D_KEYWORD return) $(D_KEYWORD this);
}
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD auto) list = $(D_KEYWORD new) List!$(D_KEYWORD int);
list.add(1).remove(1); $(D_COMMENT // error: no 'remove' method for Addable!int
)}
)
$(DDOC_BLANKLINE )
$(P Here the method $(D add) returns the base type, which doesn't implement the
$(D remove) method. The template this
parameter can be used for this purpose:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD interface) Addable(T)
{
$(D_KEYWORD final) R add($(D_KEYWORD this) R)(T t)
{
$(D_KEYWORD return) $(D_KEYWORD cast)(R)$(D_KEYWORD this); $(D_COMMENT // cast is necessary, but safe
) }
}
$(D_KEYWORD class) List(T) : Addable!T
{
List remove(T t)
{
$(D_KEYWORD return) $(D_KEYWORD this);
}
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD auto) list = $(D_KEYWORD new) List!$(D_KEYWORD int);
$(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD is)($(D_KEYWORD typeof)(list.add(1)) == List!$(D_KEYWORD int)));
list.add(1).remove(1); $(D_COMMENT // ok, List.add
)
Addable!$(D_KEYWORD int) a = list;
$(D_COMMENT // a.add calls Addable.add
) $(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD is)($(D_KEYWORD typeof)(a.add(1)) == Addable!$(D_KEYWORD int)));
}
)
)
$(DDOC_BLANKLINE )
$(LNAME2 template_value_parameter, Value Parameters)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME TemplateValueParameter):
$(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator)
$(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) $(GLINK TemplateValueParameterSpecialization)
$(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) $(GLINK TemplateValueParameterDefault)
$(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) $(GLINK TemplateValueParameterSpecialization) $(GLINK TemplateValueParameterDefault)
$(DDOC_BLANKLINE )
$(GNAME TemplateValueParameterSpecialization):
$(D :) $(GLINK2 expression, ConditionalExpression)
$(DDOC_BLANKLINE )
$(GNAME TemplateValueParameterDefault):
$(D =) $(ASSIGNEXPRESSION )
$(D =) $(GLINK2 expression, SpecialKeyword)
)
$(DDOC_BLANKLINE )
$(P A template value parameter can take an argument of any expression which can
be statically evaluated at compile time.
Template value arguments can be integer values, floating point values,
nulls, string values, array literals of template value arguments,
associative array literals of template value arguments,
or struct literals of template value arguments.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD template) foo(string s)
{
string bar() { $(D_KEYWORD return) s ~ $(D_STRING " betty"); }
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD import) std.stdio;
writeln(foo!($(D_STRING "hello")).bar()); $(D_COMMENT // prints: hello betty
)}
)
)
$(DDOC_BLANKLINE )
$(LNAME2 value_specialization, Specialization)
$(DDOC_BLANKLINE )
$(P Any specialization or default expression provided must be evaluatable
at compile-time.)
$(DDOC_BLANKLINE )
$(P In this example, template foo
has a value parameter that
is specialized for 10
:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD template) foo(U : $(D_KEYWORD int), $(D_KEYWORD int) v : 10)
{
U x = v;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD assert)(foo!($(D_KEYWORD int), 10).x == 10);
$(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD __traits)(compiles, foo!($(D_KEYWORD int), 11)));
}
)
)
$(DDOC_BLANKLINE )
$(P This can be useful when a different template body is required for a specific value.
Another template overload would be defined to take other integer literal values.)
$(DDOC_BLANKLINE )
$(LNAME2 aliasparameters, Alias Parameters)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME TemplateAliasParameter):
$(D alias) $(GLINK_LEX Identifier) $(GLINK TemplateAliasParameterSpecialization)$(OPT ) $(GLINK TemplateAliasParameterDefault)$(OPT )
$(D alias) $(GLINK2 type, BasicType) $(GLINK2 declaration, Declarator) $(GLINK TemplateAliasParameterSpecialization)$(OPT ) $(GLINK TemplateAliasParameterDefault)$(OPT )
$(DDOC_BLANKLINE )
$(GNAME TemplateAliasParameterSpecialization):
$(D :) $(GLINK2 type, Type)
$(D :) $(GLINK2 expression, ConditionalExpression)
$(DDOC_BLANKLINE )
$(GNAME TemplateAliasParameterDefault):
$(D =) $(GLINK2 type, Type)
$(D =) $(GLINK2 expression, ConditionalExpression)
)
$(DDOC_BLANKLINE )
$(P Alias parameters enable templates to be parameterized with
symbol names or values computed at compile-time.
Almost any kind of D symbol can be used, including type names,
global names, local names, module names, template names, and
template instances.
)
$(DDOC_BLANKLINE )
$(LNAME2 alias_symbol, Symbol Aliases)
$(DDOC_BLANKLINE )
$(UL $(LI Type names
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD class) Foo
{
$(D_KEYWORD static) $(D_KEYWORD int) x;
}
$(D_KEYWORD template) Bar($(D_KEYWORD alias) a)
{
$(D_KEYWORD alias) sym = a.x;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD alias) bar = Bar!(Foo);
bar.sym = 3; $(D_COMMENT // sets Foo.x to 3
) $(D_KEYWORD assert)(Foo.x == 3);
}
)
)
)
$(DDOC_BLANKLINE )
$(LI Global names
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD shared) $(D_KEYWORD int) x;
$(D_KEYWORD template) Foo($(D_KEYWORD alias) var)
{
$(D_KEYWORD auto) ptr = &var;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD alias) bar = Foo!(x);
*bar.ptr = 3; $(D_COMMENT // set x to 3
) $(D_KEYWORD assert)(x == 3);
$(D_KEYWORD static) $(D_KEYWORD shared) $(D_KEYWORD int) y;
$(D_KEYWORD alias) abc = Foo!(y);
*abc.ptr = 3; $(D_COMMENT // set y to 3
) $(D_KEYWORD assert)(y == 3);
}
)
)
)
$(DDOC_BLANKLINE )
$(LI Local names
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD template) Foo($(D_KEYWORD alias) var)
{
$(D_KEYWORD void) inc() { var++; }
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD int) v = 4;
$(D_KEYWORD alias) foo = Foo!v;
foo.inc();
$(D_KEYWORD assert)(v == 5);
}
)
)
See also $(RELATIVE_LINK2 implicit-nesting, Implicit Template Nesting).
)
$(DDOC_BLANKLINE )
$(LI Module names
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.conv;
$(D_KEYWORD template) Foo($(D_KEYWORD alias) a)
{
$(D_KEYWORD alias) sym = a.text;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD alias) bar = Foo!(std.conv);
string s = bar.sym(3); $(D_COMMENT // calls std.conv.text$(LPAREN)3$(RPAREN )
) $(D_KEYWORD assert)(s == $(D_STRING "3"));
}
)
)
)
$(DDOC_BLANKLINE )
$(LI Template names
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD shared) $(D_KEYWORD int) x;
$(D_KEYWORD template) Foo($(D_KEYWORD alias) var)
{
$(D_KEYWORD auto) ptr = &var;
}
$(D_KEYWORD template) Bar($(D_KEYWORD alias) Tem)
{
$(D_KEYWORD alias) instance = Tem!(x);
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD alias) bar = Bar!(Foo);
*bar.instance.ptr = 3; $(D_COMMENT // sets x to 3
) $(D_KEYWORD assert)(x == 3);
}
)
)
)
$(DDOC_BLANKLINE )
$(LI Template instance names
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD shared) $(D_KEYWORD int) x;
$(D_KEYWORD template) Foo($(D_KEYWORD alias) var)
{
$(D_KEYWORD auto) ptr = &var;
}
$(D_KEYWORD template) Bar($(D_KEYWORD alias) sym)
{
$(D_KEYWORD alias) p = sym.ptr;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD alias) foo = Foo!(x);
$(D_KEYWORD alias) bar = Bar!(foo);
*bar.p = 3; $(D_COMMENT // sets x to 3
) $(D_KEYWORD assert)(x == 3);
}
)
)
)
)
$(DDOC_BLANKLINE )
$(LNAME2 alias_value, Value Aliases)
$(DDOC_BLANKLINE )
$(UL $(DDOC_BLANKLINE )
$(LI Literals
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD template) Foo($(D_KEYWORD alias) x, $(D_KEYWORD alias) y)
{
$(D_KEYWORD static) $(D_KEYWORD int) i = x;
$(D_KEYWORD static) string s = y;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD import) std.stdio;
$(D_KEYWORD alias) foo = Foo!(3, $(D_STRING "bar"));
writeln(foo.i, foo.s); $(D_COMMENT // prints 3bar
)}
)
))
$(DDOC_BLANKLINE )
$(LI Compile-time values
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD template) Foo($(D_KEYWORD alias) x)
{
$(D_KEYWORD static) $(D_KEYWORD int) i = x;
}
$(D_KEYWORD void) main()
{
$(D_COMMENT // compile-time argument evaluation
) $(D_KEYWORD enum) two = 1 + 1;
$(D_KEYWORD alias) foo = Foo!(5 * two);
$(D_KEYWORD assert)(foo.i == 10);
$(D_KEYWORD static) $(D_KEYWORD assert)(foo.stringof == $(D_STRING "Foo!10"));
$(D_COMMENT // compile-time function evaluation
) $(D_KEYWORD int) get10() { $(D_KEYWORD return) 10; }
$(D_KEYWORD alias) bar = Foo!(get10());
$(D_COMMENT // bar is the same template instance as foo
) $(D_KEYWORD assert)(&bar.i $(D_KEYWORD is) &foo.i);
}
)
))
$(DDOC_BLANKLINE )
$(LI Function Literals
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD template) Foo($(D_KEYWORD alias) fun)
{
$(D_KEYWORD enum) val = fun(2);
}
$(D_KEYWORD alias) foo = Foo!(($(D_KEYWORD int) x) => x * x);
$(D_KEYWORD static) $(D_KEYWORD assert)(foo.val == 4);
)
))
)
$(DDOC_BLANKLINE )
$(LNAME2 typed_alias_op, Typed Alias Parameters)
$(DDOC_BLANKLINE )
$(P Alias parameters can also be typed.
These parameters will accept symbols of that type:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD template) Foo($(D_KEYWORD alias) $(D_KEYWORD int) p) { $(D_KEYWORD alias) a = p; }
$(D_KEYWORD void) fun()
{
$(D_KEYWORD int) i = 0;
Foo!i.a++; $(D_COMMENT // ok
) $(D_KEYWORD assert)(i == 1);
$(D_KEYWORD float) f;
$(D_COMMENT //Foo!f; // fails to instantiate
)}
)
)
$(DDOC_BLANKLINE )
$(LNAME2 alias_parameter_specialization, Specialization)
$(DDOC_BLANKLINE )
$(P Alias parameters can accept both literals and user-defined type symbols,
but they are less specialized than the matches to type parameters and
value parameters:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) Foo(T) { ... } $(D_COMMENT // #1
)$(D_KEYWORD template) Foo($(D_KEYWORD int) n) { ... } $(D_COMMENT // #2
)$(D_KEYWORD template) Foo($(D_KEYWORD alias) sym) { ... } $(D_COMMENT // #3
)
$(D_KEYWORD struct) S {}
$(D_KEYWORD int) var;
$(D_KEYWORD alias) foo1 = Foo!(S); $(D_COMMENT // instantiates #1
)$(D_KEYWORD alias) foo2 = Foo!(1); $(D_COMMENT // instantiates #2
)$(D_KEYWORD alias) foo3a = Foo!([1,2]); $(D_COMMENT // instantiates #3
)$(D_KEYWORD alias) foo3b = Foo!(var); $(D_COMMENT // instantiates #3
))
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) Bar($(D_KEYWORD alias) A) { ... } $(D_COMMENT // #4
)$(D_KEYWORD template) Bar(T : U!V, $(D_KEYWORD alias) U, V...) { ... } $(D_COMMENT // #5
)
$(D_KEYWORD class) C(T) {}
$(D_KEYWORD alias) bar = Bar!(C!$(D_KEYWORD int)); $(D_COMMENT // instantiates #5
))
$(DDOC_BLANKLINE )
$(LNAME2 variadic-templates, Sequence Parameters)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME TemplateSequenceParameter):
$(GLINK_LEX Identifier) $(D ...)
)
$(DDOC_BLANKLINE )
$(P If the last template parameter in the $(I TemplateParameterList)
is declared as a $(I TemplateSequenceParameter),
it is a match with zero or more trailing template arguments.
Any argument that can be passed to a $(GLINK TemplateAliasParameter)
can be passed to a sequence parameter.
)
$(P Such a sequence of arguments can itself be aliased for use outside
a template. The $(REF AliasSeq, std,meta) template simply
aliases its sequence parameter:)
$(D_CODE $(D_KEYWORD alias) AliasSeq(Args...) = Args;
)
$(P A TemplateSequenceParameter will thus henceforth
be referred to by that name for clarity.
An $(I AliasSeq) is not itself a type, value, or symbol. It is a
$(DDLINK articles/ctarguments, Compile-time Sequences, compile-time sequence)
of any mix of types, values or symbols, or none.
)
$(DDOC_BLANKLINE )
$(P The elements of an $(I AliasSeq) are automatically expanded
when it is referenced in a declaration or expression.
An $(I AliasSeq) can be
$(DDSUBLINK articles/ctarguments, auto-expansion, used as arguments)
to instantiate a template.
)
$(DDOC_BLANKLINE )
$(LNAME2 homogeneous_sequences, Homogeneous Sequences)
$(DDOC_BLANKLINE )
$(UL $(LI An $(I AliasSeq) whose elements consist entirely of types is
called a type sequence or $(I TypeSeq).)
$(LI An $(I AliasSeq) whose elements consist entirely of values is
called a value sequence or $(I ValueSeq).)
$(LI typeof
can be used on a ValueSeq to obtain a TypeSeq.)
)
$(DDOC_BLANKLINE )
$(P A ValueSeq can be used as arguments to call a
function:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio : writeln;
$(D_KEYWORD template) print(args...) $(D_COMMENT // args must be a ValueSeq
){
$(D_KEYWORD void) print()
{
writeln($(D_STRING "args are "), args);
}
}
$(D_KEYWORD void) main()
{
print!(1, 'a', 6.8)(); $(D_COMMENT // prints: args are 1a6.8
)}
)
)
$(DDOC_BLANKLINE )
$(P A TypeSeq can be used to declare parameters for a function:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio : writeln;
$(D_KEYWORD template) print(Types...) $(D_COMMENT // Types must be a TypeSeq
){
$(D_KEYWORD void) print(Types args) $(D_COMMENT // args is a ValueSeq
) {
writeln($(D_STRING "args are "), args);
}
}
$(D_KEYWORD void) main()
{
print!($(D_KEYWORD int), $(D_KEYWORD char), $(D_KEYWORD double))(1, 'a', 6.8); $(D_COMMENT // prints: args are 1a6.8
)}
)
)
$(DDOC_BLANKLINE )
$(NOTE A value sequence cannot be returned from a function - instead, return a
$(REF Tuple, std,typecons).)
$(DDOC_BLANKLINE )
$(LNAME2 lvalue-sequences, Lvalue Sequences)
$(DDOC_BLANKLINE )
$(P A TypeSeq can similarly be used to
$(DDSUBLINK articles/ctarguments, type-seq-instantiation, declare variables).
Parameters or variables whose type is a TypeSeq are called an
lvalue sequence.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) main()
{
$(D_KEYWORD import) std.meta: AliasSeq;
$(D_COMMENT // use a type alias just for convenience
) $(D_KEYWORD alias) TS = AliasSeq!(string, $(D_KEYWORD int));
TS tup; $(D_COMMENT // lvalue sequence
) $(D_KEYWORD assert)(tup == AliasSeq!($(D_STRING ""), 0)); $(D_COMMENT // TS.init
)
$(D_KEYWORD int) i = 5;
$(D_COMMENT // initialize another lvalue sequence from a sequence of a value and a symbol
) $(D_KEYWORD auto) tup2 = AliasSeq!($(D_STRING "hi"), i); $(D_COMMENT // value of i is copied
) i++;
$(D_KEYWORD enum) hi5 = AliasSeq!($(D_STRING "hi"), 5); $(D_COMMENT // rvalue sequence
) $(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD is)($(D_KEYWORD typeof)(hi5) == TS));
$(D_KEYWORD assert)(tup2 == hi5);
$(D_COMMENT // lvalue sequence elements can be modified
) tup = tup2;
$(D_KEYWORD assert)(tup == hi5);
}
)
)
$(DDOC_BLANKLINE )
$(UL $(LI .tupleof
can be $(DDSUBLINK spec/class, class_properties, used on a class)
or struct instance to obtain an lvalue sequence of its fields.)
$(LI .tupleof
can be $(DDSUBLINK spec/arrays, array-properties, used on a static array)
instance to obtain an lvalue sequence of its elements.)
)
$(DDOC_BLANKLINE )
$(LNAME2 seq-ops, Sequence Operations)
$(DDOC_BLANKLINE )
$(UL $(LI The number of elements in an $(I AliasSeq) can be retrieved with
the $(D .length) property.)
$(LI The $(I n)th element can be retrieved by
$(DDSUBLINK spec/expression, index_operations, indexing) an
$(I AliasSeq) with Seq[n]
. Indexes must be known at compile-time.
The result is an lvalue when the element is a symbol which resolves to a variable,
or when the sequence is an lvalue sequence.)
$(LI $(DDSUBLINK spec/expression, slice_operations, Slicing)
produces a new sequence with a subset of the elements of the original sequence.)
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.meta : AliasSeq;
$(D_KEYWORD int) v = 4;
$(D_COMMENT // alias a sequence of 3 values and one symbol
)$(D_KEYWORD alias) nums = AliasSeq!(1, 2, 3, v);
$(D_KEYWORD static) $(D_KEYWORD assert)(nums.length == 4);
$(D_KEYWORD static) $(D_KEYWORD assert)(nums[1] == 2);
$(D_COMMENT //nums[0]++; // Error, nums[0] is an rvalue
)nums[3]++; $(D_COMMENT // OK, nums[3] is bound to v, an lvalue
)$(D_KEYWORD assert)(v == 5);
$(D_COMMENT // slice first 3 elements
)$(D_KEYWORD alias) trio = nums[0 .. $-1];
$(D_COMMENT // expand into an array literal
)$(D_KEYWORD static) $(D_KEYWORD assert)([trio] == [1, 2, 3]);
)
)
$(DDOC_BLANKLINE )
$(P $(I AliasSeq)s are static compile-time entities, there is no way
to dynamically change, add, or remove elements either at compile-time or run-time.
Instead, either:)
$(UL $(LI Construct a new sequence using the original sequence (or a slice of it) and additional elements before or after it.)
$(LI Use $(DDSUBLINK spec/declaration, AliasAssign, Alias Assignment)
to build new sequences iteratively.)
)
$(P Sequences can 'unroll' code for each element using a
$(DDSUBLINK spec/statement, foreach_over_tuples, foreach
statement).)
$(DDOC_BLANKLINE )
$(LNAME2 typeseq_deduction, Type Sequence Deduction)
$(DDOC_BLANKLINE )
$(P Type sequences can be deduced from the trailing parameters
of an $(RELATIVE_LINK2 ifti, implicitly instantiated) function template:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio;
$(D_KEYWORD template) print(T, Args...)
{
$(D_KEYWORD void) print(T first, Args args)
{
writeln(first);
$(D_KEYWORD static) $(D_KEYWORD if) (args.length) $(D_COMMENT // if more arguments
) print(args); $(D_COMMENT // recurse for remaining arguments
) }
}
$(D_KEYWORD void) main()
{
print(1, 'a', 6.8);
}
)
)
$(DDOC_BLANKLINE )
prints:
$(DDOC_BLANKLINE )
$(CONSOLE 1
a
6.8
)
$(DDOC_BLANKLINE )
$(P Type sequences can also be deduced from the type of a delegate
or function parameter list passed as a function argument:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD import) std.stdio;
$(D_COMMENT /* Partially applies a delegate by tying its first argument to a particular value.
* R = return type
* T = first argument type
* Args = TypeSeq of remaining argument types
*/)
R $(D_KEYWORD delegate)(Args) partial(R, T, Args...)(R $(D_KEYWORD delegate)(T, Args) dg, T first)
{
$(D_COMMENT // return a closure
) $(D_KEYWORD return) (Args args) => dg(first, args);
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD int) plus($(D_KEYWORD int) x, $(D_KEYWORD int) y, $(D_KEYWORD int) z)
{
$(D_KEYWORD return) x + y + z;
}
$(D_KEYWORD import) std.stdio;
$(D_KEYWORD auto) plus_two = partial(&plus, 2);
writeln(plus_two(6, 8)); $(D_COMMENT // prints 16
)}
)
)
See also: $(REF partial, std,functional)
$(DDOC_BLANKLINE )
$(LNAME2 variadic_template_specialization, Specialization)
$(DDOC_BLANKLINE )
$(P If both a template with a sequence parameter and a template
without a sequence parameter exactly match a template instantiation,
the template without a $(I TemplateSequenceParameter) is selected.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) Foo(T) { $(D_KEYWORD pragma)(msg, $(D_STRING "1")); } $(D_COMMENT // #1
)$(D_KEYWORD template) Foo($(D_KEYWORD int) n) { $(D_KEYWORD pragma)(msg, $(D_STRING "2")); } $(D_COMMENT // #2
)$(D_KEYWORD template) Foo($(D_KEYWORD alias) sym) { $(D_KEYWORD pragma)(msg, $(D_STRING "3")); } $(D_COMMENT // #3
)$(D_KEYWORD template) Foo(Args...) { $(D_KEYWORD pragma)(msg, $(D_STRING "4")); } $(D_COMMENT // #4
)
$(D_KEYWORD import) std.stdio;
$(D_COMMENT // Any sole template argument will never match to #4
)$(D_KEYWORD alias) foo1 = Foo!($(D_KEYWORD int)); $(D_COMMENT // instantiates #1
)$(D_KEYWORD alias) foo2 = Foo!(3); $(D_COMMENT // instantiates #2
)$(D_KEYWORD alias) foo3 = Foo!(std); $(D_COMMENT // instantiates #3
)
$(D_KEYWORD alias) foo4 = Foo!($(D_KEYWORD int), 3, std); $(D_COMMENT // instantiates #4
))
$(DDOC_BLANKLINE )
$(LNAME2 template_parameter_def_values, Default Arguments)
$(DDOC_BLANKLINE )
$(P Trailing template parameters can be given default arguments:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) Foo(T, U = $(D_KEYWORD int)) { ... }
Foo!($(D_KEYWORD uint),$(D_KEYWORD long)); $(D_COMMENT // instantiate Foo with T as uint, and U as long
)Foo!($(D_KEYWORD uint)); $(D_COMMENT // instantiate Foo with T as uint, and U as int
)
$(D_KEYWORD template) Foo(T, U = T*) { ... }
Foo!($(D_KEYWORD uint)); $(D_COMMENT // instantiate Foo with T as uint, and U as uint*
))
$(DDOC_BLANKLINE )
$(P See also: $(RELATIVE_LINK2 function-default, Function Template Default Arguments).)
$(DDOC_BLANKLINE )
$(LNAME2 implicit_template_properties, Eponymous Templates)
$(DDOC_BLANKLINE )
$(P If a template contains members whose name is the same as the
template identifier then these members are assumed to be referred
to in a template instantiation:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) foo(T)
{
T foo; $(D_COMMENT // declare variable foo of type T
)}
$(D_KEYWORD void) main()
{
foo!($(D_KEYWORD int)) = 6; $(D_COMMENT // instead of foo!$(LPAREN)int$(RPAREN ).foo
)}
)
$(DDOC_BLANKLINE )
$(P The following example has more than one eponymous member and uses
$(RELATIVE_LINK2 ifti, Implicit Function Template Instantiation):)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) foo(S, T)
{
$(D_COMMENT // each member contains all the template parameters
) $(D_KEYWORD void) foo(S s, T t) {}
$(D_KEYWORD void) foo(S s, T t, string) {}
}
$(D_KEYWORD void) main()
{
foo(1, 2, $(D_STRING "test")); $(D_COMMENT // foo!$(LPAREN)int, int$(RPAREN ).foo$(LPAREN)1, 2, "test"$(RPAREN )
) foo(1, 2); $(D_COMMENT // foo!$(LPAREN)int, int$(RPAREN ).foo$(LPAREN)1, 2$(RPAREN )
)}
)
$(DDOC_BLANKLINE )
$(LNAME2 aggregate_templates, Aggregate Type Templates)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME ClassTemplateDeclaration):
$(D class) $(GLINK_LEX Identifier) $(GLINK TemplateParameters) $(D ;)
$(D class) $(GLINK_LEX Identifier) $(GLINK TemplateParameters) $(GLINK Constraint)$(OPT ) $(GLINK2 class, BaseClassList)$(OPT ) $(GLINK2 struct, AggregateBody)
$(D class) $(GLINK_LEX Identifier) $(GLINK TemplateParameters) $(GLINK2 class, BaseClassList)$(OPT ) $(GLINK Constraint)$(OPT ) $(GLINK2 struct, AggregateBody)
$(DDOC_BLANKLINE )
$(GNAME InterfaceTemplateDeclaration):
$(D interface) $(GLINK_LEX Identifier) $(GLINK TemplateParameters) $(D ;)
$(D interface) $(GLINK_LEX Identifier) $(GLINK TemplateParameters) $(GLINK Constraint)$(OPT ) $(GLINK2 interface, BaseInterfaceList)$(OPT ) $(GLINK2 struct, AggregateBody)
$(D interface) $(GLINK_LEX Identifier) $(GLINK TemplateParameters) $(GLINK2 interface, BaseInterfaceList) $(GLINK Constraint) $(GLINK2 struct, AggregateBody)
$(DDOC_BLANKLINE )
$(GNAME StructTemplateDeclaration):
$(D struct) $(GLINK_LEX Identifier) $(GLINK TemplateParameters) $(D ;)
$(D struct) $(GLINK_LEX Identifier) $(GLINK TemplateParameters) $(GLINK Constraint)$(OPT ) $(GLINK2 struct, AggregateBody)
$(DDOC_BLANKLINE )
$(GNAME UnionTemplateDeclaration):
$(D union) $(GLINK_LEX Identifier) $(GLINK TemplateParameters) $(D ;)
$(D union) $(GLINK_LEX Identifier) $(GLINK TemplateParameters) $(GLINK Constraint)$(OPT ) $(GLINK2 struct, AggregateBody)
)
$(DDOC_BLANKLINE )
$(P If a template declares exactly one member, and that member is a class
with the same name as the template (see
$(RELATIVE_LINK2 implicit_template_properties, Eponymous Templates):))
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) $(CODE_HIGHLIGHT Bar)(T)
{
$(D_KEYWORD class) $(CODE_HIGHLIGHT Bar)
{
T member;
}
}
)
$(DDOC_BLANKLINE )
then the semantic equivalent, called a $(I ClassTemplateDeclaration)
can be written as:
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD class) Bar(T)
{
T member;
}
)
$(DDOC_BLANKLINE )
$(P See also: $(RELATIVE_LINK2 template_this_parameter, This Parameters).
)
$(DDOC_BLANKLINE )
$(P Analogously to class templates, struct, union and interfaces
can be transformed into templates by supplying a template parameter list.
)
$(DDOC_BLANKLINE )
$(LNAME2 function-templates, Function Templates)
$(DDOC_BLANKLINE )
$(P If a template declares exactly one member, and that member is a function
with the same name as the template, it is a function template declaration.
Alternatively, a function template declaration is a function declaration
with a $(GLINK TemplateParameterList) immediately preceding the
$(GLINK2 function, Parameters).
)
$(DDOC_BLANKLINE )
$(P A function template to compute the square of type $(I T) is:)
$(DDOC_BLANKLINE )
$(D_CODE T $(CODE_HIGHLIGHT square)(T)(T t)
{
$(D_KEYWORD return) t * t;
}
)
$(DDOC_BLANKLINE )
$(P It is lowered to:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) $(CODE_HIGHLIGHT square)(T)
{
T $(CODE_HIGHLIGHT square)(T t)
{
$(D_KEYWORD return) t * t;
}
}
)
$(DDOC_BLANKLINE )
$(P Function templates can be explicitly instantiated with
Identifier!$(LPAREN)
TemplateArgumentList$(RPAREN )
:)
$(DDOC_BLANKLINE )
$(D_CODE writefln($(D_STRING "The square of %s is %s"), 3, square!($(D_KEYWORD int))(3));
)
$(DDOC_BLANKLINE )
$(LNAME2 ifti, Implicit Function Template Instantiation (IFTI))
$(DDOC_BLANKLINE )
$(P Function templates can be $(I implicitly) instantiated if the
$(I TemplateArgumentList) is deducible from the types of the
function arguments:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE T square(T)(T t)
{
$(D_KEYWORD return) t * t;
}
writefln($(D_STRING "The square of %s is %s"), 3, square(3)); $(D_COMMENT // T is deduced to be int
))
)
$(DDOC_BLANKLINE )
$(P Type parameter deduction is not influenced by the order of function
arguments.
)
$(DDOC_BLANKLINE )
$(P If there are fewer arguments supplied in the $(I TemplateArgumentList)
than parameters in the $(I TemplateParameterList), the arguments fill
parameters from left to right, and the rest of the parameters are then deduced
from the function arguments.
)
$(DDOC_BLANKLINE )
$(LNAME2 ifti-restrictions, Restrictions)
$(DDOC_BLANKLINE )
$(P Function template type parameters that are to be implicitly
deduced must appear in the type of at least one function parameter:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) foo(T : U*, U)(U t) {}
$(D_KEYWORD void) main()
{
$(D_KEYWORD int) x;
foo!($(D_KEYWORD int)*)(x); $(D_COMMENT // ok, U is deduced and T is specified explicitly
) $(D_COMMENT //foo$(LPAREN)x$(RPAREN ); // error, only U can be deduced, not T
)}
)
)
$(DDOC_BLANKLINE )
$(P When the template parameters must be deduced, the
$(RELATIVE_LINK2 implicit_template_properties, eponymous members)
can't rely on a $(LINK2 version.html#StaticIfCondition, static if
)
condition since the deduction relies on how the members are used:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD template) foo(T)
{
$(D_KEYWORD static) $(D_KEYWORD if) ($(D_KEYWORD is)(T)) $(D_COMMENT // T is not yet known...
) $(D_KEYWORD void) foo(T t) {} $(D_COMMENT // T is deduced from the member usage
)}
$(D_KEYWORD void) main()
{
$(D_COMMENT //foo$(LPAREN)0$(RPAREN ); // Error: cannot deduce function from argument types
) foo!$(D_KEYWORD int)(0); $(D_COMMENT // Ok since no deduction necessary
)}
)
)
$(DDOC_BLANKLINE )
$(P IFTI does not work when the parameter type is an
$(RELATIVE_LINK2 alias-template, alias template) instance:
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S(T) {}
$(D_KEYWORD alias) A(T) = S!T;
$(D_KEYWORD void) f(T)(A!T) {}
$(D_KEYWORD void) main()
{
A!$(D_KEYWORD int) v;
$(D_COMMENT //f$(LPAREN)v$(RPAREN ); // error
) f!$(D_KEYWORD int)(v); $(D_COMMENT // OK
)}
)
)
$(DDOC_BLANKLINE )
$(LNAME2 ifti-conversions, Type Conversions)
$(DDOC_BLANKLINE )
$(P If template type parameters match the literal expressions on function arguments,
the deduced types may consider narrowing conversions of them.
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) foo(T)(T v) { $(D_KEYWORD pragma)(msg, $(D_STRING "in foo, T = "), T); }
$(D_KEYWORD void) bar(T)(T v, T[] a) { $(D_KEYWORD pragma)(msg, $(D_STRING "in bar, T = "), T); }
$(D_KEYWORD void) main()
{
foo(1);
$(D_COMMENT // an integer literal type is analyzed as int by default
) $(D_COMMENT // then T is deduced to int
)
$(D_KEYWORD short)[] arr;
bar(1, arr);
$(D_COMMENT // arr is short[], and the integer literal 1 is
) $(D_COMMENT // implicitly convertible to short.
) $(D_COMMENT // then T will be deduced to short.
)
bar(1, [2.0, 3.0]);
$(D_COMMENT // the array literal is analyzed as double[],
) $(D_COMMENT // and the integer literal 1 is implicitly convertible to double.
) $(D_COMMENT // then T will be deduced to double.
)}
)
)
$(DDOC_BLANKLINE )
$(P The deduced type parameter for dynamic array and pointer arguments
has an unqualified head:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD void) foo(T)(T arg) { $(D_KEYWORD pragma)(msg, T); }
$(D_KEYWORD void) test()
{
$(D_KEYWORD int)[] marr;
$(D_KEYWORD const)($(D_KEYWORD int)[]) carr;
$(D_KEYWORD immutable)($(D_KEYWORD int)[]) iarr;
foo(marr); $(D_COMMENT // T == int[]
) foo(carr); $(D_COMMENT // T == const$(LPAREN)int$(RPAREN )[]
) foo(iarr); $(D_COMMENT // T == immutable$(LPAREN)int$(RPAREN )[]
)
$(D_KEYWORD int)* mptr;
$(D_KEYWORD const)($(D_KEYWORD int)*) cptr;
$(D_KEYWORD immutable)($(D_KEYWORD int)*) iptr;
foo(mptr); $(D_COMMENT // T == int*
) foo(cptr); $(D_COMMENT // T == const$(LPAREN)int$(RPAREN )*
) foo(iptr); $(D_COMMENT // T == immutable$(LPAREN)int$(RPAREN )*
)}
)
)
$(DDOC_BLANKLINE )
$(LNAME2 return-deduction, Return Type Deduction)
$(DDOC_BLANKLINE )
$(P Function templates can have their return types deduced based on the
$(GLINK2 statement, ReturnStatement)s in the function, just as with
normal functions.
See $(DDSUBLINK spec/function, auto-functions, Auto Functions).
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD auto) square(T)(T t)
{
$(D_KEYWORD return) t * t;
}
$(D_KEYWORD auto) i = square(2);
$(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD is)($(D_KEYWORD typeof)(i) == $(D_KEYWORD int)));
)
)
$(DDOC_BLANKLINE )
$(LNAME2 auto-ref-parameters, Auto Ref Parameters)
$(DDOC_BLANKLINE )
$(P Template functions can have auto ref parameters.
An auto ref parameter becomes a ref parameter
if its corresponding argument is an lvalue, otherwise it becomes
a value parameter:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD int) countRefs(Args...)($(D_KEYWORD auto) $(D_KEYWORD ref) Args args)
{
$(D_KEYWORD int) result;
$(D_KEYWORD foreach) (i, _; args)
{
$(D_KEYWORD if) ($(D_KEYWORD __traits)(isRef, args[i]))
result++;
}
$(D_KEYWORD return) result;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD int) y;
$(D_KEYWORD assert)(countRefs(3, 4) == 0);
$(D_KEYWORD assert)(countRefs(3, y, 4) == 1);
$(D_KEYWORD assert)(countRefs(y, 6, y) == 2);
}
)
)
$(DDOC_BLANKLINE )
$(P Auto ref parameters can be combined with auto ref return
attributes:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD auto) $(D_KEYWORD ref) min(T, U)($(D_KEYWORD auto) $(D_KEYWORD ref) T lhs, $(D_KEYWORD auto) $(D_KEYWORD ref) U rhs)
{
$(D_KEYWORD return) lhs > rhs ? rhs : lhs;
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD int) i;
i = min(4, 3);
$(D_KEYWORD assert)(i == 3);
$(D_KEYWORD int) x = 7, y = 8;
i = min(x, y);
$(D_KEYWORD assert)(i == 7);
$(D_COMMENT // result is an lvalue
) min(x, y) = 10; $(D_COMMENT // sets x to 10
) $(D_KEYWORD assert)(x == 10 && y == 8);
$(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD __traits)(compiles, min(3, y) = 10));
$(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD __traits)(compiles, min(y, 3) = 10));
}
)
)
$(DDOC_BLANKLINE )
$(LNAME2 function-default, Default Arguments)
$(DDOC_BLANKLINE )
$(P Template arguments not implicitly deduced can have default values:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD void) $(CODE_HIGHLIGHT foo)(T, U=T*)(T t) { U p; ... }
$(D_KEYWORD int) x;
foo(x); $(D_COMMENT // T is int, U is int*
))
$(DDOC_BLANKLINE )
$(P Variadic Function Templates can have parameters with default values.
These parameters are always set to their default value in case of IFTI.
)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE size_t fun(T...)(T t, string file = $(D_KEYWORD __FILE__))
{
$(D_KEYWORD import) std.stdio;
writeln(file, $(D_STRING " "), t);
$(D_KEYWORD return) T.length;
}
$(D_KEYWORD assert)(fun(1, $(D_STRING "foo")) == 2); $(D_COMMENT // uses IFTI
)$(D_KEYWORD assert)(fun!$(D_KEYWORD int)(1, $(D_STRING "filename")) == 1); $(D_COMMENT // no IFTI
))
)
$(DDOC_BLANKLINE )
$(LNAME2 template_ctors, Template Constructors)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME ConstructorTemplate):
$(D this) $(GLINK TemplateParameters) $(GLINK2 function, Parameters) $(GLINK2 function, MemberFunctionAttributes)$(OPT ) $(GLINK Constraint)$(OPT ) $(GLINK2 function, FunctionBody)
)
$(DDOC_BLANKLINE )
$(P Templates can be used to form constructors for classes and structs.
)
$(DDOC_BLANKLINE )
$(LNAME2 variable-template, Enum & Variable Templates)
$(DDOC_BLANKLINE )
$(P Like aggregates and functions, $(DDSUBLINK spec/declaration, variable-declarations, variable
declarations) and manifest constants can have template parameters, providing there is
an $(GLINK2 declaration, Initializer):)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD enum) $(D_KEYWORD bool) within($(D_KEYWORD alias) v, T) = v <= T.max && v >= T.min;
$(D_KEYWORD ubyte)[T.sizeof] storage(T) = 0;
$(D_KEYWORD const) triplet($(D_KEYWORD alias) v) = [v, v+1, v+2];
$(D_KEYWORD static) $(D_KEYWORD assert)(within!(-128F, $(D_KEYWORD byte)));
$(D_KEYWORD static) $(D_KEYWORD assert)(storage!($(D_KEYWORD int)[2]).length == 8);
$(D_KEYWORD static) $(D_KEYWORD assert)(triplet!3 == [3, 4, 5]);
)
)
$(DDOC_BLANKLINE )
$(P Those declarations are transformed into these TemplateDeclarations:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) within($(D_KEYWORD alias) v, T)
{
$(D_KEYWORD enum) $(D_KEYWORD bool) within = v <= T.max && v >= T.min;
}
$(D_KEYWORD template) storage(T)
{
$(D_KEYWORD ubyte)[T.sizeof] storage = 0;
}
$(D_KEYWORD template) triplet($(D_KEYWORD alias) v)
{
$(D_KEYWORD const) triplet = [v, v+1, v+2];
}
)
$(DDOC_BLANKLINE )
$(LNAME2 alias-template, Alias Templates)
$(DDOC_BLANKLINE )
$(P $(GLINK2 declaration, AliasDeclaration) can also have optional template
parameters:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD alias) ElementType(T : T[]) = T;
$(D_KEYWORD alias) Sequence(TL...) = TL;
)
$(DDOC_BLANKLINE )
It is lowered to:
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) ElementType(T : T[])
{
$(D_KEYWORD alias) ElementType = T;
}
$(D_KEYWORD template) Sequence(TL...)
{
$(D_KEYWORD alias) Sequence = TL;
}
)
$(DDOC_BLANKLINE )
$(LNAME2 nested-templates, Nested Templates)
$(DDOC_BLANKLINE )
$(P If a template is declared in aggregate or function local scope, the
instantiated functions will implicitly capture the context of the
enclosing scope.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD class) C
{
$(D_KEYWORD int) num;
$(D_KEYWORD this)($(D_KEYWORD int) n) { num = n; }
$(D_KEYWORD template) Foo()
{
$(D_COMMENT // 'foo' can access 'this' reference of class C object.
) $(D_KEYWORD void) foo($(D_KEYWORD int) n) { $(D_KEYWORD this).num = n; }
}
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD auto) c = $(D_KEYWORD new) C(1);
$(D_KEYWORD assert)(c.num == 1);
c.Foo!().foo(5);
$(D_KEYWORD assert)(c.num == 5);
$(D_KEYWORD template) Bar()
{
$(D_COMMENT // 'bar' can access local variable of 'main' function.
) $(D_KEYWORD void) bar($(D_KEYWORD int) n) { c.num = n; }
}
Bar!().bar(10);
$(D_KEYWORD assert)(c.num == 10);
}
)
)
$(DDOC_BLANKLINE )
$(P Above, $(D Foo!().foo) will work just the same as a final
member function
of class $(D C), and $(D Bar!().bar) will work just the same as a nested
function within function $(D main$(LPAREN)$(RPAREN )).)
$(DDOC_BLANKLINE )
$(LNAME2 limitations, Aggregate Type Limitations)
$(DDOC_BLANKLINE )
$(P A nested template cannot add non-static fields to an aggregate type.
Fields declared in a nested template will be implicitly static
.)
$(P A nested template cannot add virtual functions to a class or interface.
Methods inside a nested template will be implicitly final
.)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD class) Foo
{
$(D_KEYWORD template) TBar(T)
{
T xx; $(D_COMMENT // becomes a static field of Foo
) $(D_KEYWORD void) func(T) {} $(D_COMMENT // implicitly final
) $(D_COMMENT //abstract void baz$(LPAREN)$(RPAREN ); // error, final functions cannot be abstract
)
$(D_KEYWORD static) T yy; $(D_COMMENT // Ok
) $(D_KEYWORD static) $(D_KEYWORD void) func(T t, $(D_KEYWORD int) y) {} $(D_COMMENT // Ok
) }
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD alias) bar = Foo.TBar!$(D_KEYWORD int);
bar.xx++;
$(D_COMMENT //bar.func$(LPAREN)1$(RPAREN ); // error, no this
)
$(D_KEYWORD auto) o = $(D_KEYWORD new) Foo;
o.TBar!$(D_KEYWORD int).func(1); $(D_COMMENT // OK
)}
)
)
$(DDOC_BLANKLINE )
$(LNAME2 implicit-nesting, Implicit Nesting)
$(DDOC_BLANKLINE )
$(P If a template has a $(RELATIVE_LINK2 aliasparameters, template alias parameter),
and is instantiated with a local symbol, the instantiated function will
implicitly become nested in order to access runtime data of the given
local symbol.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD template) Foo($(D_KEYWORD alias) sym)
{
$(D_KEYWORD void) foo() { sym = 10; }
}
$(D_KEYWORD class) C
{
$(D_KEYWORD int) num;
$(D_KEYWORD this)($(D_KEYWORD int) n) { num = n; }
$(D_KEYWORD void) main()
{
$(D_KEYWORD assert)($(D_KEYWORD this).num == 1);
$(D_KEYWORD alias) fooX = Foo!(C.num).foo;
$(D_COMMENT // fooX will become member function implicitly, so &fooX
) $(D_COMMENT // returns a delegate object.
) $(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD is)($(D_KEYWORD typeof)(&fooX) == $(D_KEYWORD delegate)));
fooX(); $(D_COMMENT // called by using valid 'this' reference.
) $(D_KEYWORD assert)($(D_KEYWORD this).num == 10); $(D_COMMENT // OK
) }
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD new) C(1).main();
$(D_KEYWORD int) num;
$(D_KEYWORD alias) fooX = Foo!num.foo;
$(D_COMMENT // fooX will become nested function implicitly, so &fooX
) $(D_COMMENT // returns a delegate object.
) $(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD is)($(D_KEYWORD typeof)(&fooX) == $(D_KEYWORD delegate)));
fooX();
$(D_KEYWORD assert)(num == 10); $(D_COMMENT // OK
)}
)
$(DDOC_BLANKLINE )
$(P Not only functions, but also instantiated class and struct types can
become nested via implicitly captured context.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD class) C
{
$(D_KEYWORD int) num;
$(D_KEYWORD this)($(D_KEYWORD int) n) { num = n; }
$(D_KEYWORD class) N(T)
{
$(D_COMMENT // instantiated class N!T can become nested in C
) T foo() { $(D_KEYWORD return) num * 2; }
}
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD auto) c = $(D_KEYWORD new) C(10);
$(D_KEYWORD auto) n = c.$(D_KEYWORD new) N!$(D_KEYWORD int)();
$(D_KEYWORD assert)(n.foo() == 20);
}
)
$(DDOC_BLANKLINE )
$(P See also: $(DDSUBLINK spec/class, nested-explicit, Nested Class Instantiation).)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD void) main()
{
$(D_KEYWORD int) num = 10;
$(D_KEYWORD struct) S(T)
{
$(D_COMMENT // instantiated struct S!T can become nested in main$(LPAREN)$(RPAREN )
) T foo() { $(D_KEYWORD return) num * 2; }
}
S!$(D_KEYWORD int) s;
$(D_KEYWORD assert)(s.foo() == 20);
}
)
$(DDOC_BLANKLINE )
$(P A templated $(D struct) can become a nested $(D struct) if it
is instantiated with a local symbol passed as an aliased argument:)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD struct) A($(D_KEYWORD alias) F)
{
$(D_KEYWORD int) fun($(D_KEYWORD int) i) { $(D_KEYWORD return) F(i); }
}
A!F makeA($(D_KEYWORD alias) F)() { $(D_KEYWORD return) A!F(); }
$(D_KEYWORD void) main()
{
$(D_KEYWORD int) x = 40;
$(D_KEYWORD int) fun($(D_KEYWORD int) i) { $(D_KEYWORD return) x + i; }
A!fun a = makeA!fun();
$(D_KEYWORD assert)(a.fun(2) == 42);
}
)
$(DDOC_BLANKLINE )
$(LNAME2 nested_template_limitation, Context Limitation)
$(DDOC_BLANKLINE )
$(P Currently nested templates can capture at most one context. As a typical
example, non-static template member functions cannot take local symbol
by using template alias parameter.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD class) C
{
$(D_KEYWORD int) num;
$(D_KEYWORD void) foo($(D_KEYWORD alias) sym)() { num = sym * 2; }
}
$(D_KEYWORD void) main()
{
$(D_KEYWORD auto) c = $(D_KEYWORD new) C();
$(D_KEYWORD int) var = 10;
c.foo!var(); $(D_COMMENT // NG, foo!var requires two contexts, 'this' and 'main$(LPAREN)$(RPAREN )'
)}
)
$(DDOC_BLANKLINE )
$(P But, if one context is indirectly accessible from other context, it is allowed.)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD int) sum($(D_KEYWORD alias) x, $(D_KEYWORD alias) y)() { $(D_KEYWORD return) x + y; }
$(D_KEYWORD void) main()
{
$(D_KEYWORD int) a = 10;
$(D_KEYWORD void) nested()
{
$(D_KEYWORD int) b = 20;
$(D_KEYWORD assert)(sum!(a, b)() == 30);
}
nested();
}
)
$(DDOC_BLANKLINE )
$(P Two local variables $(D a) and $(D b) are in different contexts, but
outer context is indirectly accessible from innter context, so nested
template instance $(D sum!$(LPAREN)a, b$(RPAREN )) will capture only
inner context.)
$(DDOC_BLANKLINE )
$(LNAME2 recursive_templates, Recursive Templates)
$(DDOC_BLANKLINE )
$(P Template features can be combined to produce some interesting
effects, such as compile time evaluation of non-trivial functions.
For example, a factorial template can be written:)
$(DDOC_BLANKLINE )
$(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD template) factorial($(D_KEYWORD int) n)
{
$(D_KEYWORD static) $(D_KEYWORD if) (n == 1)
$(D_KEYWORD enum) factorial = 1;
$(D_KEYWORD else)
$(D_KEYWORD enum) factorial = n * factorial!(n - 1);
}
$(D_KEYWORD static) $(D_KEYWORD assert)(factorial!(4) == 24);
)
)
$(P For more information and a $(ACRONYM CTFE, Compile-time Function Execution)
factorial alternative, see:
$(DDSUBLINK articles/templates-revisited, template-recursion, Template Recursion).
)
$(DDOC_BLANKLINE )
$(LNAME2 template_constraints, Template Constraints)
$(DDOC_BLANKLINE )
$(GRAMMAR $(GNAME Constraint):
$(D if) $(D $(LPAREN)) $(GLINK2 expression, Expression) $(D $(RPAREN ))
)
$(DDOC_BLANKLINE )
$(P $(I Constraint)s are used to impose additional constraints
on matching arguments to a template beyond what is possible
in the $(GLINK TemplateParameterList).
The $(I Expression) is computed at compile time
and returns a result that is converted to a boolean value.
If that value is true, then the template is matched,
otherwise the template is not matched.
)
$(DDOC_BLANKLINE )
$(P For example, the following function template only
matches with odd values of $(CODE N):)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD void) foo($(D_KEYWORD int) N)()
$(D_KEYWORD if) (N & 1)
{
...
}
...
foo!(3)(); $(D_COMMENT // OK, matches
)foo!(4)(); $(D_COMMENT // Error, no match
))
$(DDOC_BLANKLINE )
$(P Template constraints can be used with aggregate types (structs, classes, unions).
Constraints are effectively used with library module $(MREF std, traits):)
$(DDOC_BLANKLINE )
$(D_CODE $(D_KEYWORD import) std.traits;
$(D_KEYWORD struct) Bar(T)
$(D_KEYWORD if) (isIntegral!T)
{
...
}
...
$(D_KEYWORD auto) x = Bar!$(D_KEYWORD int); $(D_COMMENT // OK, int is an integral type
)$(D_KEYWORD auto) y = Bar!$(D_KEYWORD double); $(D_COMMENT // Error, double does not satisfy constraint
))
$(DDOC_BLANKLINE )
$(SPEC_SUBNAV_PREV_NEXT operatoroverloading, Operator Overloading, template-mixin, Template Mixins)
)
)