C++ Templates
Templates are recipes teaching the compiler how to make functions, classes, variables, and type aliases from ingredients (types and values) 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.
template<typename T>
void func()
{
// ...
}
template<typename T>
class Class
{
// ...
};
template<typename T>
T var;
template<typename T>
using alias = T;
Templates can be defined in namespace or class scope, but not within a function.
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, 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() {}
};
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][itanium-cxx-abi], which defines separate complete object and base object constructors.