Movatterモバイル変換


[0]ホーム

URL:


D Logo
Menu
Search

Language Reference

table of contents

Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page.Requires a signed-in GitHub account. This works well for small changes.If you'd like to make larger changes you may want to consider usinga local clone.

Templates

Contents
  1. Template Declarations
  2. Template Instantiation
    1. Explicit Template Instantiation
    2. Common Instantiation
    3. Practical Example
    4. Instantiation Scope
  3. Template Parameters
    1. Type Parameters
    2. This Parameters
    3. Value Parameters
    4. Alias Parameters
    5. Sequence Parameters
    6. Default Arguments
  4. Eponymous Templates
  5. Aggregate Type Templates
  6. Function Templates
    1. Implicit Function Template Instantiation (IFTI)
    2. Return Type Deduction
    3. Auto Ref Parameters
    4. Default Arguments
  7. Template Constructors
  8. Enum & Variable Templates
  9. Alias Templates
  10. Nested Templates
    1. Aggregate Type Limitations
    2. Implicit Nesting
    3. Context Limitation
  11. Recursive Templates
  12. Template Constraints

Template Declarations

Templates are D's approach to generic programming. Templates can be defined with aTemplateDeclaration:

TemplateDeclaration:templateIdentifierTemplateParametersConstraintopt{DeclDefsopt}
TemplateParameters:(TemplateParameterListopt)
TemplateParameterList:TemplateParameterTemplateParameter,TemplateParameter,TemplateParameterList

TheDeclDefs 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.

Template parameters can take types, values, symbols, or sequences.

template t(T)// declare type parameter T{    T v;// declare a member variable of type T within template t}

If multiple templates with the sameIdentifier are declared, they are distinct if they have different parameters or are differently specialized.

If a template has a member which has the same identifier as the template, the template is anEponymous Template.template declarations with one eponymous member are usually written as specificshort syntax template declarations instead.

Template Instantiation

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.

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.

Explicit Template Instantiation

Templates are explicitly instantiated using a! after the template name, then either an argument list or a single token argument.

TemplateInstance:IdentifierTemplateArguments
TemplateArguments:! (TemplateArgumentListopt)!TemplateSingleArgument
TemplateArgumentList:TemplateArgumentTemplateArgument,TemplateArgument,TemplateArgumentList
TemplateSingleArgument:IdentifierFundamentalTypeCharacterLiteralStringLiteralInterpolationExpressionSequenceIntegerLiteralFloatLiteraltruefalsenullthisSpecialKeyword

A template argument can be a type, compile-time expression or a symbol.

TemplateArgument:TypeAssignExpressionSymbol
Symbol:SymbolTail.SymbolTail
SymbolTail:IdentifierIdentifier.SymbolTailTemplateInstanceTemplateInstance.SymbolTail

Once instantiated, the declarations inside the template, called the template members, are in the scope of theTemplateInstance:

template TFoo(T) {alias Ptr = T*; }...TFoo!(int).Ptr x;// declare x to be of type int*

If theTemplateArgument is one token long, the parentheses can be omitted:

TFoo!int.Ptr x;// same as TFoo!(int).Ptr x;

A template instantiation can be aliased:

template TFoo(T) {alias Ptr = T*; }alias foo = TFoo!(int);foo.Ptr x;// declare x to be of type int*

Common Instantiation

Multiple instantiations of aTemplateDeclaration with the sameTemplateArgumentList will all refer to the same instantiation. For example:

template TFoo(T) { T f; }alias a = TFoo!(int);alias b = TFoo!(int);...a.f = 3;assert(b.f == 3);// a and b refer to the same instance of TFoo

This is true even if theTemplateInstances are done in different modules.

Even if template arguments are implicitly converted to the same template parameter type, they still refer to the same instance. This example uses aTemplateValueParameter and astruct template:

struct TFoo(int x) { }// Different template parameters create different struct typesstaticassert(!is(TFoo!(3) == TFoo!(2)));// 3 and 2+1 are both 3 of type int - same TFoo instancestaticassert(is(TFoo!(3) == TFoo!(2 + 1)));// 3u is implicitly converted to 3 to match int parameter,// and refers to exactly the same instance as TFoo!(3)staticassert(is(TFoo!(3) == TFoo!(3u)));

Practical Example

A simple generic copy template would be:

template TCopy(T){void copy(out T to, T from)    {        to = from;    }}

To use this template, it must first be instantiated with a specific type:

int i;TCopy!(int).copy(i, 3);

See alsofunction templates.

Instantiation Scope

TemplateInstances are always instantiated in the scope of where theTemplateDeclaration is declared, with the addition of the template parameters being declared as aliases for their deduced types.

Example:

module a:
template TFoo(T) {void bar() { func(); } }
module b:
import a;void func() { }alias f = TFoo!(int);// error: func not defined in module a

Example:

module a:
template TFoo(T) {void bar() { func(1); } }void func(double d) { }
module b:
import a;void func(int i) { }alias f = TFoo!(int);...f.bar();// will call a.func(double)

TemplateParameter specializations and default arguments are evaluated in the scope of theTemplateDeclaration.

Template Parameters

TemplateParameter:TemplateTypeParameterTemplateValueParameterTemplateAliasParameterTemplateSequenceParameterTemplateThisParameter

Template parameters can take types, values, symbols, or sequences.

A template parameter can have aspecialization which constrains an argument theTemplateParameter can accept.

template t(T :int)// type T must implicitly convert to int{    ...}

A default argument specifies the type, value or symbol to use for theTemplateParameter when a matching argument is not supplied.

Type Parameters

TemplateTypeParameter:IdentifierTemplateTypeParameterSpecializationoptTemplateTypeParameterDefaultopt
TemplateTypeParameterSpecialization::Type
TemplateTypeParameterDefault:=Type

Specialization and Pattern Matching

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:

template TFoo(T)        { ... }// #1template TFoo(T : T[])  { ... }// #2template TFoo(T :char) { ... }// #3template TFoo(T, U, V)  { ... }// #4alias foo1 = TFoo!(int);// instantiates #1alias foo2 = TFoo!(double[]);// instantiates #2 matching pattern T[] with T being doublealias foo3 = TFoo!(char);// instantiates #3alias fooe = TFoo!(char,int);// error, number of arguments mismatchalias foo4 = TFoo!(char,int,int);// instantiates #4

The template picked to instantiate is the one that is most specialized that fits the types of theTemplateArgumentList. If the result is ambiguous, it is an error.

Type Parameter Deduction

The types of template parameters are deduced for a particular template instantiation by comparing the template argument with the corresponding template parameter.

For each template parameter, the following rules are applied in order until a type is deduced for each parameter:

  1. If there is no type specialization for the parameter, the type of the parameter is set to the template argument.
  2. 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.
  3. 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 theTemplateArgumentList.
  4. If applying the above rules does not result in exactly one type for each template parameter, then it is an error.

For example:

template TFoo(T) { }alias foo1 = TFoo!(int);// (1) T is deduced to be intalias foo2 = TFoo!(char*);// (1) T is deduced to be char*template TBar(T : T*) { }// match template argument against T* patternalias bar = TBar!(char*);// (2) T is deduced to be chartemplate TAbc(D, U : D[]) { }// D[] is pattern to be matchedalias abc1 = TAbc!(int,int[]);// (2) D is deduced to be int, U is int[]alias abc2 = TAbc!(char,int[]);// (4) error, D is both char and inttemplate TDef(D : E*, E) { }// E* is pattern to be matchedalias def = TDef!(int*,int);// (1) E is int// (3) D is int*

Deduction from a specialization can provide values for more than one parameter:

template Foo(T: T[U], U){    ...}Foo!(int[long])// instantiates Foo with T set to int, U set to long

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 foo = TFoo!(B);// (3) T is Btemplate TBar(T : U*, U : A) { }alias bar = TBar!(B*, B);// (2) T is B*// (3) U is B

This Parameters

TemplateThisParameter:thisTemplateTypeParameter

TemplateThisParameters are used in member function templates to pick up the type of thethis reference. It also will infer the mutability of thethis reference. For example, ifthis isconst, then the function is markedconst.

struct S{void foo(this T)()const    {pragma(msg, T);    }}void main(){const(S) s;    (&s).foo();    S s2;    s2.foo();immutable(S) s3;    s3.foo();}
Prints:
const(S)Simmutable(S)

Avoiding Runtime Type Checks

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:

interface Addable(T){finalauto add(T t)    {returnthis;    }}class List(T) : Addable!T{    List remove(T t)    {returnthis;    }}void main(){auto list =new List!int;    list.add(1).remove(1);// error: no 'remove' method for Addable!int}

Here the methodadd returns the base type, which doesn't implement theremove method. The templatethis parameter can be used for this purpose:

interface Addable(T){final R add(this R)(T t)    {returncast(R)this;// cast is necessary, but safe    }}class List(T) : Addable!T{    List remove(T t)    {returnthis;    }}void main(){auto list =new List!int;staticassert(is(typeof(list.add(1)) == List!int));    list.add(1).remove(1);// ok, List.add    Addable!int a = list;// a.add calls Addable.addstaticassert(is(typeof(a.add(1)) == Addable!int));}

Value Parameters

TemplateValueParameter:BasicTypeDeclaratorTemplateValueParameterSpecializationoptTemplateValueParameterDefaultopt
TemplateValueParameterSpecialization::ConditionalExpression
TemplateValueParameterDefault:=AssignExpression=SpecialKeyword

A template value parameter can take an argument of any expression which can be statically evaluated at compile time as one of the following:

template foo(string s){enum string bar = s ~" betty";}void main(){import std.stdio;    writeln(foo!("hello").bar);// prints: hello betty}

Specialization

Any specialization or default expression provided must be evaluatable at compile-time.

In this example, templatefoo has a value parameter that is specialized for10:

template foo(U :int,int v : 10){    U x = v;}void main(){assert(foo!(int, 10).x == 10);staticassert(!__traits(compiles, foo!(int, 11)));}

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.

Alias Parameters

TemplateAliasParameter:aliasIdentifierTemplateAliasParameterSpecializationoptTemplateAliasParameterDefaultoptaliasBasicTypeDeclaratorTemplateAliasParameterSpecializationoptTemplateAliasParameterDefaultopt
TemplateAliasParameterSpecialization::Type:ConditionalExpression
TemplateAliasParameterDefault:=Type=ConditionalExpression

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.

Symbol Aliases

Value Aliases

Typed Alias Parameters

Alias parameters can also be typed. These parameters will accept symbols of that type:

template Foo(aliasint p) {alias a = p; }void fun(){int i = 0;    Foo!i.a++;// okassert(i == 1);float f;//Foo!f;  // fails to instantiate}

Specialization

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:

template Foo(T)         { ... }// #1template Foo(int n)     { ... }// #2template Foo(alias sym) { ... }// #3struct S {}int var;alias foo1  = Foo!(S);// instantiates #1alias foo2  = Foo!(1);// instantiates #2alias foo3a = Foo!([1,2]);// instantiates #3alias foo3b = Foo!(var);// instantiates #3
template Bar(alias A) { ... }// #4template Bar(T : U!V,alias U, V...) { ... }// #5class C(T) {}alias bar = Bar!(C!int);// instantiates #5

Sequence Parameters

TemplateSequenceParameter:Identifier...

If the last template parameter in theTemplateParameterList is declared as aTemplateSequenceParameter, it is a match with zero or more trailing template arguments. Any argument that can be passed to aTemplateAliasParameter can be passed to a sequence parameter.

Such a sequence of arguments can itself be aliased for use outside a template. Thestd.meta.AliasSeq template simply aliases its sequence parameter:

alias AliasSeq(Args...) = Args;

ATemplateSequenceParameter will thus henceforth be referred to by that name for clarity. AnAliasSeq is not itself a type, value, or symbol. It is acompile-time sequence of any mix of types, values or symbols, or none.

The elements of anAliasSeq are automatically expanded when it is referenced in a declaration or expression. AnAliasSeq can beused as arguments to instantiate a template.

Homogeneous Sequences

AValueSeq can be used as arguments to call a function:

import std.stdio : writeln;template print(args...)// args must be a ValueSeq{void print()    {        writeln("args are ", args);    }}void main(){    print!(1, 'a', 6.8)();// prints: args are 1a6.8}

ATypeSeq can be used to declare a parameter sequence for a function:

import std.stdio : writeln;template print(Types...)// Types must be a TypeSeq{void print(Types args)// args is a ValueSeq    {        writeln("args are ", args);    }}void main(){    print!(int,char,double)(1, 'a', 6.8);// prints: args are 1a6.8}

A value sequence:

Lvalue Sequences

ATypeSeq can similarly be used todeclare variables. A variable whose type is aTypeSeq is called anlvalue sequence.

import std.meta: AliasSeq;// use a type alias just for conveniencealias TS = AliasSeq!(string,int);TS tup;// lvalue sequenceassert(tup == AliasSeq!("", 0));// TS.init// elements can be modifiedtup[1]++;assert(tup[1] == 1);byte i = 5;// initialize another lvalue sequence from a sequence of a value and a symbolauto tup2 = AliasSeq!("hi", i);// value of i is copiedi++;assert(tup2[1] == 5);// unchangedenum hi5 = AliasSeq!("hi", 5);// rvalue sequencestaticassert(is(typeof(hi5) == TS));// compare elementsassert(tup2 == hi5);// OK, byte and int have a common type// lvalue sequence can be assigned to a ValueSeqtup = tup2;assert(tup == hi5);

An lvalue sequence can be initialized from a single expression. Each element is initialized from the given expression.

import std.meta: AliasSeq;AliasSeq!(int,int,int) vs = 4;assert(vs == AliasSeq!(4, 4, 4));int[3] sa = [1, 2, 3];vs = sa.tupleof;assert(vs == AliasSeq!(1, 2, 3));

Sequence Operations

import std.meta : AliasSeq;int v = 4;// alias a sequence of 3 values and one symbolalias nums = AliasSeq!(1, 2, 3, v);staticassert(nums.length == 4);staticassert(nums[1] == 2);//nums[0]++; // Error, nums[0] is an rvaluenums[3]++;// OK, nums[3] is bound to v, an lvalueassert(v == 5);// slice first 3 elementsalias trio = nums[0 .. $-1];// expand into an array literalstaticassert([trio] == [1, 2, 3]);

AliasSeqs 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:

Sequences can 'unroll' code for each element using aforeach statement.

Type Sequence Deduction

Type sequences can be deduced from the trailing parameters of animplicitly instantiated function template:

import std.stdio;template print(T, Args...){void print(T first, Args args)    {        writeln(first);staticif (args.length)// if more arguments            print(args);// recurse for remaining arguments    }}void main(){// Calls `print` with:// T = int, Args = (char, double)// T = char, Args = (double)// T = double, Args = ()    print(1, 'a', 6.8);}
prints:
1a6.8

Type sequences can also be deduced from the parameter list of afunction pointer or delegate passed as a function argument:

size_t arity(R, Args...)(Rfunction(Args) fp) => Args.length;int f(int);void g(string, Object);staticassert(arity((){}) == 0);// R = void, Args = ()staticassert(arity(&f) == 1);// R = int, Args = (int)staticassert(arity(&g) == 2);// R = void, Args = (string, Object)

Deduction can partially match a parameter list:

/* 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 */Rdelegate(Args) partial(R, T, Args...)(Rfunction(T, Args) dg, T first){// return a closurereturn (Args args) => dg(first, args);}int plus(int x,int y,int z) => x + y + z;void main(){auto plus_two = partial(&plus, 2);// R = int, T = int, Args = (int, int)assert(plus_two(6, 8) == 16);}

See also:std.functional.partial.

Specialization

If both a template with a sequence parameter and a template without a sequence parameter exactly match a template instantiation, the template without aTemplateSequenceParameter is selected.

template Foo(T)         {pragma(msg,"1"); }// #1template Foo(int n)     {pragma(msg,"2"); }// #2template Foo(alias sym) {pragma(msg,"3"); }// #3template Foo(Args...)   {pragma(msg,"4"); }// #4import std.stdio;// Any sole template argument will never match to #4alias foo1 = Foo!(int);// instantiates #1alias foo2 = Foo!(3);// instantiates #2alias foo3 = Foo!(std);// instantiates #3alias foo4 = Foo!(int, 3, std);// instantiates #4

Default Arguments

Trailing template parameters can be given default arguments:

template Foo(T, U =int) { ... }Foo!(uint,long);// instantiate Foo with T as uint, and U as longFoo!(uint);// instantiate Foo with T as uint, and U as inttemplate Foo(T, U = T*) { ... }Foo!(uint);// instantiate Foo with T as uint, and U as uint*

See also:Function Template Default Arguments.

Eponymous Templates

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:

template foo(T){    T foo;// declare variable foo of type T}void main(){    foo!(int) = 6;// instead of foo!(int).foo}

The following example has more than one eponymous member and usesImplicit Function Template Instantiation:

template foo(S, T){// each member contains all the template parametersvoid foo(S s, T t) {}void foo(S s, T t, string) {}}void main(){    foo(1, 2,"test");// foo!(int, int).foo(1, 2, "test")    foo(1, 2);// foo!(int, int).foo(1, 2)}

Aggregate Type Templates

ClassTemplateDeclaration:classIdentifierTemplateParameters;classIdentifierTemplateParametersConstraintoptBaseClassListoptAggregateBodyclassIdentifierTemplateParametersBaseClassListoptConstraintoptAggregateBody
InterfaceTemplateDeclaration:interfaceIdentifierTemplateParameters;interfaceIdentifierTemplateParametersConstraintoptBaseInterfaceListoptAggregateBodyinterfaceIdentifierTemplateParametersBaseInterfaceListConstraintAggregateBody
StructTemplateDeclaration:structIdentifierTemplateParameters;structIdentifierTemplateParametersConstraintoptAggregateBody
UnionTemplateDeclaration:unionIdentifierTemplateParameters;unionIdentifierTemplateParametersConstraintoptAggregateBody

If a template declares exactly one member, and that member is a class with the same name as the template (seeEponymous Templates:)

templateBar(T){classBar    {        T member;    }}
then the semantic equivalent, called aClassTemplateDeclaration can be written as:
class Bar(T){    T member;}

See also:This Parameters.

Analogously to class templates, struct, union and interfaces can be transformed into templates by supplying a template parameter list.

Function Templates

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 aTemplateParameterList immediately preceding theParameters.

A function template to compute the square of typeT is:

Tsquare(T)(T t){return t * t;}

It is lowered to:

templatesquare(T){    Tsquare(T t)    {return t * t;    }}

Function templates can be explicitly instantiated withIdentifier!(TemplateArgumentList):

writefln("The square of %s is %s", 3, square!(int)(3));

Implicit Function Template Instantiation (IFTI)

Function templates can beimplicitly instantiated if theTemplateArgumentList is deducible from the types of the function arguments:

T square(T)(T t){return t * t;}writefln("The square of %s is %s", 3, square(3));// T is deduced to be int

Type parameter deduction is not influenced by the order of function arguments.

If there are fewer arguments supplied in theTemplateArgumentList than parameters in theTemplateParameterList, the arguments fill parameters from left to right, and the rest of the parameters are then deduced from the function arguments.

Restrictions

Function template type parameters that are to be implicitly deduced must appear in the type of at least one function parameter:

void foo(T : U*, U)(U t) {}void main(){int x;    foo!(int*)(x);// ok, U is deduced and T is specified explicitly//foo(x);        // error, only U can be deduced, not T}

When the template parameters must be deduced, theeponymous members can't rely on astatic if condition since the deduction relies on how the members are used:

template foo(T){staticif (is(T))// T is not yet known...void foo(T t) {}// T is deduced from the member usage}void main(){//foo(0); // Error: cannot deduce function from argument types    foo!int(0);// Ok since no deduction necessary}

IFTI does not work when the parameter type is analias template instance:

struct S(T) {}alias A(T) = S!T;void f(T)(A!T) {}void main(){    A!int v;//f(v); // error    f!int(v);// OK}

ATypeSeq template parametercan be deduced from function arguments.

Type Conversions

If template type parameters match the literal expressions on function arguments, the deduced types may consider narrowing conversions of them.

void foo(T)(T v)        {pragma(msg,"in foo, T = ", T); }void bar(T)(T v, T[] a) {pragma(msg,"in bar, T = ", T); }void main(){    foo(1);// an integer literal type is analyzed as int by default// then T is deduced to intshort[] arr;    bar(1, arr);// arr is short[], and the integer literal 1 is// implicitly convertible to short.// then T will be deduced to short.    bar(1, [2.0, 3.0]);// the array literal is analyzed as double[],// and the integer literal 1 is implicitly convertible to double.// then T will be deduced to double.}

The deduced type parameter for dynamic array and pointer arguments has an unqualified head:

void foo(T)(T arg) {pragma(msg, T); }void test(){int[] marr;const(int[]) carr;immutable(int[]) iarr;    foo(marr);// T == int[]    foo(carr);// T == const(int)[]    foo(iarr);// T == immutable(int)[]int* mptr;const(int*) cptr;immutable(int*) iptr;    foo(mptr);// T == int*    foo(cptr);// T == const(int)*    foo(iptr);// T == immutable(int)*}

Return Type Deduction

Function templates can have their return types deduced based on theReturnStatements in the function, just as with normal functions. SeeAuto Functions.

auto square(T)(T t){return t * t;}auto i = square(2);staticassert(is(typeof(i) ==int));

Auto Ref Parameters

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:

int countRefs(Args...)(autoref Args args){int result;foreach (i, _; args)    {if (__traits(isRef, args[i]))            result++;    }return result;}void main(){int y;assert(countRefs(3, 4) == 0);assert(countRefs(3, y, 4) == 1);assert(countRefs(y, 6, y) == 2);}

Auto ref parameters can be combined with auto ref return attributes:

autoref min(T, U)(autoref T lhs,autoref U rhs){return lhs > rhs ? rhs : lhs;}void main(){int i;    i = min(4, 3);assert(i == 3);int x = 7, y = 8;    i = min(x, y);assert(i == 7);// result is an lvalue    min(x, y) = 10;// sets x to 10assert(x == 10 && y == 8);staticassert(!__traits(compiles, min(3, y) = 10));staticassert(!__traits(compiles, min(y, 3) = 10));}

Default Arguments

Template arguments not implicitly deduced can have default values:

voidfoo(T, U=T*)(T t) { U p; ... }int x;foo(x);// T is int, U is int*

Variadic Function Templates can have parameters with default values. These parameters are always set to their default value in case of IFTI.

size_t fun(T...)(T t, string file =__FILE__){import std.stdio;    writeln(file," ", t);return T.length;}assert(fun(1,"foo") == 2);// uses IFTIassert(fun!int(1,"filename") == 1);// no IFTI

Template Constructors

ConstructorTemplate:thisTemplateParametersParametersMemberFunctionAttributesoptConstraintoptFunctionBodythisTemplateParametersParametersMemberFunctionAttributesoptConstraintoptMissingFunctionBody

Templates can be used to form constructors for classes and structs.

Enum & Variable Templates

Like aggregates and functions,variable declarations and manifest constants can have template parameters, providing there is anInitializer:

enumbool within(alias v, T) = v <= T.max && v >= T.min;ubyte[T.sizeof] storage(T) = 0;const triplet(alias v) = [v, v+1, v+2];staticassert(within!(-128F,byte));staticassert(storage!(int[2]).length == 8);staticassert(triplet!3 == [3, 4, 5]);

Those declarations are transformed into theseTemplateDeclarations:

template within(alias v, T){enumbool within = v <= T.max && v >= T.min;}template storage(T){ubyte[T.sizeof] storage = 0;}template triplet(alias v){const triplet = [v, v+1, v+2];}

Alias Templates

AliasDeclaration can also have optional template parameters:

alias ElementType(T : T[]) = T;alias Sequence(TL...) = TL;
It is lowered to:
template ElementType(T : T[]){alias ElementType = T;}template Sequence(TL...){alias Sequence = TL;}

Nested Templates

If a template is declared in aggregate or function local scope, the instantiated functions will implicitly capture the context of the enclosing scope.

class C{int num;this(int n) { num = n; }template Foo()    {// 'foo' can access 'this' reference of class C object.void foo(int n) {this.num = n; }    }}void main(){auto c =new C(1);assert(c.num == 1);    c.Foo!().foo(5);assert(c.num == 5);template Bar()    {// 'bar' can access local variable of 'main' function.void bar(int n) { c.num = n; }    }    Bar!().bar(10);assert(c.num == 10);}

Above,Foo!().foo will work just the same as afinal member function of classC, andBar!().bar will work just the same as a nested function within functionmain().

Aggregate Type Limitations

A nested template cannot add non-static fields to an aggregate type. Fields declared in a nested template will be implicitlystatic.

A nested template cannot add virtual functions to a class or interface. Methods inside a nested template will be implicitlyfinal.

class Foo{template TBar(T)    {        T xx;// becomes a static field of Foovoid func(T) {}// implicitly final//abstract void baz(); // error, final functions cannot be abstractstatic T yy;// Okstaticvoid func(T t,int y) {}// Ok    }}void main(){alias bar = Foo.TBar!int;    bar.xx++;//bar.func(1); // error, no thisauto o =new Foo;    o.TBar!int.func(1);// OK}

Implicit Nesting

If a template has atemplate 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.

template Foo(alias sym){void foo() { sym = 10; }}class C{int num;this(int n) { num = n; }void main()    {assert(this.num == 1);alias fooX = Foo!(C.num).foo;// fooX will become member function implicitly, so &fooX//     returns a delegate object.staticassert(is(typeof(&fooX) ==delegate));        fooX();// called by using valid 'this' reference.assert(this.num == 10);// OK    }}void main(){new C(1).main();int num;alias fooX = Foo!num.foo;// fooX will become nested function implicitly, so &fooX//     returns a delegate object.staticassert(is(typeof(&fooX) ==delegate));    fooX();assert(num == 10);// OK}

Not only functions, but also instantiated class and struct types can become nested via implicitly captured context.

class C{int num;this(int n) { num = n; }class N(T)    {// instantiated class N!T can become nested in C        T foo() {return num * 2; }    }}void main(){auto c =new C(10);auto n = c.new N!int();assert(n.foo() == 20);}

See also:Nested Class Instantiation.

void main(){int num = 10;struct S(T)    {// instantiated struct S!T can become nested in main()        T foo() {return num * 2; }    }    S!int s;assert(s.foo() == 20);}

A templatedstruct can become a nestedstruct if it is instantiated with a local symbol passed as an aliased argument:

struct A(alias F){int fun(int i) {return F(i); }}A!F makeA(alias F)() {return A!F(); }void main(){int x = 40;int fun(int i) {return x + i; }    A!fun a = makeA!fun();assert(a.fun(2) == 42);}

Context Limitation

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.

class C{int num;void foo(alias sym)() { num = sym * 2; }}void main(){auto c =new C();int var = 10;    c.foo!var();// NG, foo!var requires two contexts, 'this' and 'main()'}

But, if one context is indirectly accessible from other context, it is allowed.

int sum(alias x,alias y)() {return x + y; }void main(){int a = 10;void nested()    {int b = 20;assert(sum!(a, b)() == 30);    }    nested();}

Two local variablesa andb are in different contexts, but outer context is indirectly accessible from innter context, so nested template instancesum!(a, b) will capture only inner context.

Recursive Templates

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:

template factorial(int n){staticif (n == 1)enum factorial = 1;elseenum factorial = n * factorial!(n - 1);}staticassert(factorial!(4) == 24);

For more information and aCTFE (Compile-time Function Execution) factorial alternative, see:Template Recursion.

Template Constraints

Constraint:if(Expression)

Constraints are used to impose additional constraints on matching arguments to a template beyond what is possible in theTemplateParameterList. TheExpression 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.

For example, the following function template only matches with odd values ofN:

void foo(int N)()if (N & 1){    ...}...foo!(3)();// OK, matchesfoo!(4)();// Error, no match

Template constraints can be used with aggregate types (structs, classes, unions). Constraints are effectively used with library modulestd.traits:

import std.traits;struct Bar(T)if (isIntegral!T){    ...}...auto x = Bar!int;// OK, int is an integral typeauto y = Bar!double;// Error, double does not satisfy constraint
Operator Overloading
Template Mixins
)
Copyright © 1999-2026 by theD Language Foundation | Page generated byDdoc on Fri Feb 20 23:46:44 2026

[8]ページ先頭

©2009-2026 Movatter.jp