C++ Templates

Templates are recipes teaching the compiler how to make functions, classes, variables, and type aliases from ingredients provided by the user at compile time.

Declaring and defining templates

Template functions, classes, variables, and type aliases are declared and defined just like their non-template counterparts, except preceded with template<Params...>, where Params... is a list of one or more template parameters.

/*
 * Declarations.
 */
template<typename T>
void func();

template<typename T, typename U, typename V>
class Class;

template<typename T>
extern T var;

/*
 * Definitions.
 */
template<typename T>
void func()
{
    // ...
}

template<typename T, typename U, typename V>
class Class
{
    // ...
};

template<typename T>
T var;

template<typename T>
using Alias = Class<T>;

All four types of template (function, class, variable, and type alias) can be declared (and defined) in namespace scope (including the global namespace, as above) or in class scope.

Template class members

Template classes can contain static and non-static (i.e., instance) data and function members, just as with ordinary (non-template) classes. They can also contain template members (function, class, static variable, and type alias).

To refer to template class members from outside the template class definition, the member name is preceded by template<Params...>, just as in the template class declaration, and the template class name is followed by an additional <...> containing the template parameter names:

template<typename T>
class Bar
{
public:
    // Ordinary function member
    Bar();

    // Ordinary non-static data member
    int i;

    // Ordinary static data member
    static int si;

    // Template function member
    template<typename U>
    void func();

    // Nested template class
    template<typename U>
    class Baz
    {
    public:
        // Ordinary function member
        Baz();

        // Ordinary static data member
        static int sj;
    };
};

template<typename T>
Bar<T>::Bar()
{
    // ...
}

template<typename T>
int Bar<T>::si;

template<typename T>
template<typename U>
Bar<T>::Baz<U>::Baz()
{
    // ...
}

template<typename T>
template<typename U>
int Bar<T>::Baz<U>::sj;

Nested template member references require multiple template<Params...> prefixes, starting with the outermost template and working in.

Template parameters

Template instantiation

Templates are instantiated when concrete types (or non-type values) are substituted for the template's parameters. For function templates, the substitution is often implicit. For class templates, the subsitution is explicitly given by the user.

template<typename T>
void func(T x)
{
    // ...
}

template<typename T>
class Foo
{
public:
    Foo() {}

    T foo;
};

int main()
{
    // Instantiation with T = int
    func(0);
    
    // Instantiation with T = double
    func(0.0);
    
    // Instantiation with T = bool
    func(true);
    
    // Instantiation with T = int
    Foo<int> fi;
    
    // Instantiation with T = double
    Foo<double> fd;
    
    // Instantiation with T = bool
    Foo<bool> fb;
}

The compiler generates code for each template instantiation, as can be seen with nm --demangle:

0000000000000070 T void func<bool>(bool)
0000000000000060 T void func<double>(double)
0000000000000050 T void func<int>(int)
00000000000000c0 T Foo<bool>::Foo()
0000000000000100 T Foo<bool>::Foo()
00000000000000a0 T Foo<double>::Foo()
00000000000000f0 T Foo<double>::Foo()
0000000000000080 T Foo<int>::Foo()
00000000000000e0 T Foo<int>::Foo()
0000000000000000 T _main

The fact that two versions of the constructor (seemingly with the same name) are generated for each template class is a peculiarity of the Itanium C++ ABI, which defines separate complete object and base object constructors, as can be seen in the mangled symbols:

00000000000000c0 T __ZN3FooIbEC1Ev
0000000000000100 T __ZN3FooIbEC2Ev
00000000000000a0 T __ZN3FooIdEC1Ev
00000000000000f0 T __ZN3FooIdEC2Ev
0000000000000080 T __ZN3FooIiEC1Ev
00000000000000e0 T __ZN3FooIiEC2Ev

The compiler only instantiates template class members that are used somewhere in the program. Code is not generated for unused member functions.

template<typename T>
class Foo
{
public:

    void used()
    {
    }

    void unused()
    {
    }
};

int main()
{
    Foo<int> foo;

    foo.used();
}
0000000000000020 T Foo<int>::used()
0000000000000000 T _main