Templates - D Programming Language

Menu

- Learn
- Documentation
  - Language Reference
  - Library Reference
  - Command-line Reference
  - Feature Overview
  - Articles
- Downloads
- Packages
- Community
  - Blog
  - Orgs using D
  - Twitter
  - Calendar
  - Forums
  - IRC
  - Community Discord
  - Wiki
  - GitHub
  - Issues
  - Get involved
  - Contributors
  - Foundation
  - Security Team
  - Donate
  - Sponsors
- Resources
  - Tour
  - Books
  - Tutorials
  - Tools
  - Editors
  - IDEs
  - run.dlang.io
  - Visual D
  - Acknowledgments
  - D Style
  - Glossary
  - Sitemap

Search
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 using a local clone.

# Templates

Contents

1. Explicit Template Instantiation
   1. Practical Example
2. Instantiation Scope
3. Argument Deduction
4. Template Type Parameters
   1. Specialization
5. Template This Parameters
6. Template Value Parameters
7. Template Alias Parameters
   1. Typed alias parameters
   2. Specialization
8. Template Sequence Parameters
   1. Specialization
9. Template Parameter Default Values
10. Eponymous Templates
11. Template Constructors
12. Aggregate Templates
13. Function Templates
14. Variable Templates
15. Alias Templates
    1. Function Templates with Auto Ref Parameters
16. Nested Templates
    1. Implicit Nesting
    2. Context Limitation
17. Recursive Templates
18. Template Constraints
19. Limitations

Templates are D's approach to generic programming. Templates are defined with a TemplateDeclaration:

    TemplateDeclaration:
        template Identifier TemplateParameters Constraintopt { DeclDefsopt }

    TemplateParameters:
        ( TemplateParameterListopt )

    TemplateParameterList:
        TemplateParameter
        TemplateParameter ,
        TemplateParameter , TemplateParameterList

    TemplateParameter:
        TemplateTypeParameter
        TemplateValueParameter
        TemplateAliasParameter
        TemplateSequenceParameter
        TemplateThisParameter

The body of the TemplateDeclaration must be syntactically correct even if never instantiated. Semantic analysis is not done until instantiated. A template forms its own scope, and the template body can contain classes, structs, types, enums, variables, functions, and other templates.

Template parameters can be types, values, symbols, or sequences. Types can be any type. Value parameters must be of an integral type, floating point type, or string type and specializations for them must resolve to an integral constant, floating point constant, null, or a string literal. Symbols can be any non-local symbol. Sequences can contain zero or more types, values or symbols.

Template parameter specializations constrain the values or types the TemplateParameter can accept.

Template parameter defaults are the value or type to use for the TemplateParameter in case one is not supplied.

## Explicit Template Instantiation

Templates are explicitly instantiated with:

    TemplateInstance:
        Identifier TemplateArguments

    TemplateArguments:
        ! ( TemplateArgumentListopt )
        ! TemplateSingleArgument

    TemplateArgumentList:
        TemplateArgument
        TemplateArgument ,
        TemplateArgument , TemplateArgumentList

    TemplateArgument:
        Type
        AssignExpression
        Symbol

    Symbol:
        SymbolTail
        . SymbolTail

    SymbolTail:
        Identifier
        Identifier . SymbolTail
        TemplateInstance
        TemplateInstance . SymbolTail

    TemplateSingleArgument:
        Identifier
        BasicTypeX
        CharacterLiteral
        StringLiteral
        IntegerLiteral
        FloatLiteral
        true
        false
        null
        this
        SpecialKeyword

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

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

If the TemplateArgument 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*

Multiple instantiations of a TemplateDeclaration with the same TemplateArgumentList 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 the TemplateInstances 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 a struct template:

    struct TFoo(int x) { }

    // Different template parameters create different struct types
    static assert(!is(TFoo!(3) == TFoo!(2)));
    // 3 and 2+1 are both 3 of type int - same TFoo instance
    static assert(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)
    static assert(is(TFoo!(3) == TFoo!(3u)));

If multiple templates with the same Identifier are declared, they are distinct if they have a different number of arguments or are differently specialized.

### 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 also function templates.

## Instantiation Scope

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

For 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

and:
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 values are evaluated in the scope of the TemplateDeclaration.

## Argument 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 the TemplateArgumentList.
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 int
    alias foo2 = TFoo!(char*);   // (1) T is deduced to be char*

    template TBar(T : T*) { }
    alias bar = TBar!(char*);    // (2) T is deduced to be char

    template TAbc(D, U : D[]) { }
    alias 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 int

    template TDef(D : E*, E) { }
    alias 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 B

    template TBar(T : U*, U : A) { }
    alias bar = TBar!(B*, B);  // (2) T is B*
                               // (3) U is B

## Template Type Parameters

    TemplateTypeParameter:
        Identifier
        Identifier TemplateTypeParameterSpecialization
        Identifier TemplateTypeParameterDefault
        Identifier TemplateTypeParameterSpecialization TemplateTypeParameterDefault

    TemplateTypeParameterSpecialization:
        : Type

    TemplateTypeParameterDefault:
        = Type

### Specialization

Templates may be specialized for particular types of arguments by following the template parameter identifier with a : and the specialized type. For example:

    template TFoo(T)        { ... } // #1
    template TFoo(T : T[])  { ... } // #2
    template TFoo(T : char) { ... } // #3
    template TFoo(T, U, V)  { ... } // #4

    alias foo1 = TFoo!(int);            // instantiates #1
    alias foo2 = TFoo!(double[]);       // instantiates #2 with T being double
    alias foo3 = TFoo!(char);           // instantiates #3
    alias fooe = TFoo!(char, int);      // error, number of arguments mismatch
    alias foo4 = TFoo!(char, int, int); // instantiates #4

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

## Template This Parameters

    TemplateThisParameter:
        this TemplateTypeParameter

TemplateThisParameters are used in member function templates to pick up the type of the this reference.

    import std.stdio;

    struct S
    {
        const void foo(this T)(int i)
        {
            writeln(typeid(T));
        }
    }

    void main()
    {
        const(S) s;
        (&s).foo(1);
        S s2;
        s2.foo(2);
        immutable(S) s3;
        s3.foo(3);
    }

Prints:

    const(S)
    S
    immutable(S)

This is especially useful when used with inheritance. For example, you might want to implement a final base method which returns a derived class type. Typically you would return a base type, but this won't allow you to call or access derived properties of the type:

    interface Addable(T)
    {
        final auto add(T t)
        {
            return this;
        }
    }

    class List(T) : Addable!T
    {
        List remove(T t)
        {
            return this;
        }
    }

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

Here the method add returns the base type, which doesn't implement the remove method. The template this parameter can be used for this purpose:

    interface Addable(T)
    {
        final R add(this R)(T t)
        {
            return cast(R)this;  // cast is necessary, but safe
        }
    }

    class List(T) : Addable!T
    {
        List remove(T t)
        {
            return this;
        }
    }

    void main()
    {
        auto list = new List!int;
        list.add(1).remove(1);  // ok
    }

## Template Value Parameters

    TemplateValueParameter:
        BasicType Declarator
        BasicType Declarator TemplateValueParameterSpecialization
        BasicType Declarator TemplateValueParameterDefault
        BasicType Declarator TemplateValueParameterSpecialization TemplateValueParameterDefault

    TemplateValueParameterSpecialization:
        : ConditionalExpression

    TemplateValueParameterDefault:
        = AssignExpression
        = SpecialKeyword

Template value parameter types can be any type which can be statically initialized 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.

    template foo(string s)
    {
        string bar() { return s ~ " betty"; }
    }

    void main()
    {
        writefln("%s", foo!("hello").bar()); // prints: hello betty
    }

This example of template foo has a value parameter that is specialized for 10:

    template foo(U : int, int v : 10)
    {
        U x = v;
    }

    void main()
    {
        assert(foo!(int, 10).x == 10);
    }

## Template Alias Parameters

    TemplateAliasParameter:
        alias Identifier TemplateAliasParameterSpecializationopt TemplateAliasParameterDefaultopt
        alias BasicType Declarator TemplateAliasParameterSpecializationopt TemplateAliasParameterDefaultopt

    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 user-defined type names, global names, local names, module names, template names, and template instance names.

Symbol examples:

- User-defined type names

      class Foo
      {
          static int x;
      }

      template Bar(alias a)
      {
          alias sym = a.x;
      }

      void main()
      {
          alias bar = Bar!(Foo);
          bar.sym = 3;  // sets Foo.x to 3
      }

- Global names

      shared int x;

      template Foo(alias var)
      {
          auto ptr = &var;
      }

      void main()
      {
          alias bar = Foo!(x);
          *bar.ptr = 3;       // set x to 3
          static shared int y;
          alias abc = Foo!(y);
          *abc.ptr = 3;       // set y to 3
      }

- Local names

      template Foo(alias var)
      {
          void inc() { var++; }
      }

      void main()
      {
          int v = 4;
          alias foo = Foo!v;
          foo.inc();
          assert(v == 5);
      }

  See also Implicit Template Nesting.
- Module names

      import std.conv;

      template Foo(alias a)
      {
          alias sym = a.text;
      }

      void main()
      {
          alias bar = Foo!(std.conv);
          bar.sym(3);   // calls std.conv.text(3)
      }

- Template names

      shared int x;

      template Foo(alias var)
      {
          auto ptr = &var;
      }

      template Bar(alias Tem)
      {
          alias instance = Tem!(x);
      }

      void main()
      {
          alias bar = Bar!(Foo);
          *bar.instance.ptr = 3;  // sets x to 3
      }

- Template instance names

      shared int x;

      template Foo(alias var)
      {
          auto ptr = &var;
      }

      template Bar(alias sym)
      {
          alias p = sym.ptr;
      }

      void main()
      {
          alias foo = Foo!(x);
          alias bar = Bar!(foo);
          *bar.p = 3;  // sets x to 3
      }

Value examples:

- Literals

      template Foo(alias x, alias y)
      {
          static int i = x;
          static string s = y;
      }

      void main()
      {
          alias foo = Foo!(3, "bar");
          writeln(foo.i, foo.s);  // prints 3bar
      }

- Compile-time values

      template Foo(alias x)
      {
          static int i = x;
      }

      void main()
      {
          // compile-time argument evaluation
          enum two = 1 + 1;
          alias foo = Foo!(5 * two);
          assert(foo.i == 10);
          static assert(foo.stringof == "Foo!10");

          // compile-time function evaluation
          int get10() { return 10; }
          alias bar = Foo!(get10());
          // bar is the same template instance as foo
          assert(&bar.i is &foo.i);
      }

- Lambdas

      template Foo(alias fun)
      {
          enum val = fun(2);
      }

      alias foo = Foo!((int x) => x * x);
      static assert(foo.val == 4);

### Typed alias parameters

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

    template Foo(alias int x) { }
    int x;
    float f;

    Foo!x;  // ok
    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)         { ... }  // #1
    template Foo(int n)     { ... }  // #2
    template Foo(alias sym) { ... }  // #3

    struct S {}
    int var;

    alias foo1  = Foo!(S);      // instantiates #1
    alias foo2  = Foo!(1);      // instantiates #2
    alias foo3a = Foo!([1,2]);  // instantiates #3
    alias foo3b = Foo!(var);    // instantiates #3

    template Bar(alias A) { ... }                 // #4
    template Bar(T : U!V, alias U, V...) { ... }  // #5

    class C(T) {}
    alias bar = Bar!(C!int);    // instantiates #5

## Template Sequence Parameters

    TemplateSequenceParameter:
        Identifier ...

If the last template parameter in the TemplateParameterList is declared as a TemplateSequenceParameter, it is a match with any trailing template arguments. Such a sequence of arguments can be defined using the template std.meta.AliasSeq and will thus henceforth be referred to by that name for clarity. An AliasSeq is not itself a type, value, or symbol. It is a compile-time sequence of any mix of types, values or symbols.

An AliasSeq whose elements consist entirely of types is called a type sequence or TypeSeq. An AliasSeq whose elements consist entirely of values is called a value sequence or ValueSeq.

An AliasSeq can be used as an argument list to instantiate another template, or as the list of parameters for a function.

    template print(args...)
    {
        void print()
        {
            writeln("args are ", args); // args is a ValueSeq
        }
    }

    template write(Args...)
    {
        void write(Args args) // Args is a TypeSeq
                              // args is a ValueSeq
        {
            writeln("args are ", args);
        }
    }

    void main()
    {
        print!(1,'a',6.8).print();                    // prints: args are 1a6.8
        write!(int, char, double).write(1, 'a', 6.8); // prints: args are 1a6.8
    }

The number of elements in an AliasSeq can be retrieved with the .length property. The nth element can be retrieved by indexing the AliasSeq with [n], and sub-sequences are denoted by the slicing syntax.

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.

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

    template print(T, Args...)
    {
        void print(T first, Args args)
        {
            writeln(first);
            static if (args.length) // if more arguments
                print(args);        // recurse for remaining arguments
        }
    }

    void main()
    {
        print(1, 'a', 6.8);
    }

prints:

    1
    a
    6.8

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

    import std.stdio;

    /* 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 delegate(Args) partial(R, T, Args...)(R delegate(T, Args) dg, T first)
    {
        // return a closure
        return (Args args) => dg(first, args);
    }

    void main()
    {
        int plus(int x, int y, int z)
        {
            return x + y + z;
        }

        auto plus_two = partial(&plus, 2);
        writefln("%d", plus_two(6, 8)); // prints 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 a TemplateSequenceParameter is selected.

    template Foo(T)         { pragma(msg, "1"); }   // #1
    template Foo(int n)     { pragma(msg, "2"); }   // #2
    template Foo(alias sym) { pragma(msg, "3"); }   // #3
    template Foo(Args...)   { pragma(msg, "4"); }   // #4

    import std.stdio;

    // Any sole template argument will never match to #4
    alias foo1 = Foo!(int);          // instantiates #1
    alias foo2 = Foo!(3);            // instantiates #2
    alias foo3 = Foo!(std);          // instantiates #3

    alias foo4 = Foo!(int, 3, std);  // instantiates #4

## Template Parameter Default Values

Trailing template parameters can be given default values:

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

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

## Eponymous Templates

If a template contains members whose name is the same as the template identifier and if the type or the parameters type of these members include at least all the template parameters 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
    }

Using functions and more types than the template:

    template foo(S, T)
    {
        // each member contains all the template parameters
        void 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)
    }

When the template parameters must be deduced, the eponymous members can't rely on a static if condition since the deduction relies on how the in members are used:

    template foo(T)
    {
        static if (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
    }

## Template Constructors

    ConstructorTemplate:
        this TemplateParameters Parameters MemberFunctionAttributesopt Constraintopt :
        this TemplateParameters Parameters MemberFunctionAttributesopt Constraintopt FunctionBody

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

## Aggregate Templates

    ClassTemplateDeclaration:
        class Identifier TemplateParameters ;
        class Identifier TemplateParameters Constraintopt BaseClassListopt AggregateBody
        class Identifier TemplateParameters BaseClassListopt Constraintopt AggregateBody

    InterfaceTemplateDeclaration:
        interface Identifier TemplateParameters ;
        interface Identifier TemplateParameters Constraintopt BaseInterfaceListopt AggregateBody
        interface Identifier TemplateParameters BaseInterfaceList Constraint AggregateBody

    StructTemplateDeclaration:
        struct Identifier TemplateParameters ;
        struct Identifier TemplateParameters Constraintopt AggregateBody

    UnionTemplateDeclaration:
        union Identifier TemplateParameters ;
        union Identifier TemplateParameters Constraintopt AggregateBody

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

    template Bar(T)
    {
        class Bar
        {
            T member;
        }
    }

then the semantic equivalent, called a ClassTemplateDeclaration can be written as:

    class Bar(T)
    {
        T member;
    }

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 a TemplateParameterList immediately preceding the Parameters.

A function template to compute the square of type T is:

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

It is lowered to:

    template Square(T)
    {
        T Square(T t)
        {
            return t * t;
        }
    }

Function templates can be explicitly instantiated with a !(TemplateArgumentList):

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

or implicitly, where the TemplateArgumentList is deduced from the types of the function arguments:

    writefln("The square of %s is %s", 3, Square(3));  // T is deduced to be int

If there are fewer arguments supplied in the TemplateArgumentList than parameters in the TemplateParameterList, the arguments fulfill parameters from left to right, and the rest of the parameters are then deduced from the function arguments.

The process of deducing template type parameters from function arguments is called Implicit Function Template Instantiation (IFTI).

Function template type parameters that are to be implicitly deduced may not have specializations:

    void Foo(T : T*)(T t) { ... }

    int x,y;
    Foo!(int*)(x);   // ok, T is not deduced from function argument
    Foo(&y);         // error, T has specialization

Template arguments not implicitly deduced can have default values:

    void Foo(T, U=T*)(T t) { U p; ... }

    int x;
    Foo(x);    // T is int, U is int*

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); }

    foo(1);
    // an integer literal type is analyzed as int by default
    // then T is deduced to int

    short[] arr;
    bar(1, arr);
    // arr is short[], and the interger 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 interger 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); }

    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)*

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

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

    auto Square(T)(T t)
    {
        return t * t;
    }

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 IFTI
    assert(fun!int(1, "foo") == 1);  // no IFTI

## Variable Templates

Same as aggregates and functions, variable declarations with Initializer can have optional template parameters:

    enum string constant(TL...) = TL.stringof;
    ubyte[T.sizeof] storage(T) = 0;
    auto array(alias a) = a;

These declarations are transformed into templates:

    template constant(TL...)
    {
        enum string constant = TL.stringof;
    }
    template storage(T)
    {
        ubyte[T.sizeof] storage = 0;
    }
    template array(alias a)
    {
        auto array = a;
    }

## Alias Templates

AliasDeclaration can also have optional template parameters:

    alias Sequence(TL...) = TL;

It is lowered to:

    template Sequence(TL...)
    {
        alias Sequence = TL;
    }

### Function Templates with Auto Ref Parameters

An auto ref function template parameter becomes a ref parameter if its corresponding argument is an lvalue, otherwise it becomes a value parameter:

    int foo(Args...)(auto ref Args args)
    {
        int result;

        foreach (i, v; args)
        {
            if (v == 10)
                assert(__traits(isRef, args[i]));
            else
                assert(!__traits(isRef, args[i]));
            result += v;
        }
        return result;
    }

    void main()
    {
        int y = 10;
        int r;
        r = foo(8);       // returns 8
        r = foo(y);       // returns 10
        r = foo(3, 4, y); // returns 17
        r = foo(4, 5, y); // returns 19
        r = foo(y, 6, y); // returns 26
    }

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

    auto ref min(T, U)(auto ref T lhs, auto ref U rhs)
    {
        return lhs > rhs ? rhs : lhs;
    }

    void main()
    {
        int x = 7, y = 8;
        int i;

        i = min(4, 3);     // returns 3
        i = min(x, y);     // returns 7
        min(x, y) = 10;    // sets x to 10
        static assert(!__traits(compiles, min(3, y) = 10));
        static assert(!__traits(compiles, min(y, 3) = 10));
    }

## 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 a member function of class C, and Bar!().bar will work just the same as a nested function within function main().

### Implicit Nesting

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

    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.
            static assert(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.
        static assert(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);
    }

    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 templated struct can become a nested struct 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 variables a and b are in different contexts, but outer context is indirectly accessible from innter context, so nested template instance sum!(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 : 1)
    {
        enum { factorial = 1 }
    }

    template factorial(int n)
    {
        enum { factorial = n* factorial!(n-1) }
    }

    void test()
    {
        writefln("%s", factorial!(4)); // prints 24
    }

## Template Constraints

    Constraint:
        if ( Expression )

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

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

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

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

    import std.traits;

    struct Bar(T)
        if (isIntegral!T)
    {
        ...
    }
    ...
    auto x = Bar!int;       // OK, int is an integral type
    auto y = Bar!double;    // Error, double does not satisfy constraint

## Limitations

Templates cannot be used to add non-static fields or virtual functions to classes or interfaces. For example:

    class Foo
    {
        template TBar(T)
        {
            T xx;               // becomes a static field of Foo
            int func(T) { ... } // non-virtual

            static T yy;                        // Ok
            static int func(T t, int y) { ... } // Ok
        }
    }

Operator Overloading
Template Mixins
Copyright © 1999-2019 by the D Language Foundation | Page generated by Ddoc on Wed Sep 18 02:27:26 2019