C++ is more than a mere object-oriented programming language. The full power of C++ is seen when programming with templates. This style of programming is sometimes called generic programming or metaprogramming.
Just as a class is a pattern for creating objects as instances of the class, templates are patterns for creating classes or functions as instances of the template. A template takes one or more parameters, and the instantiated classes or functions supply arguments for the parameters. The classes and functions can have different behavior or implementations, depending on the arguments.
Programming with templates is unlike traditional object-oriented programming. Object-oriented programming centers around type polymorphism (requiring classes, objects, and virtual functions). Template-based programming centers around parametric polymorphism, where a function or class is defined independently of its parameters (which can be values, types, or even other templates).
This chapter describes the syntax and semantics of declaring, specializing, instantiating, and using templates. See Chapter 9 for information about some typical uses of templates in the standard library. See Appendix ### for information about Loki and other examples of template-oriented projects.
A template declaration can be a function declaration, function definition, class declaration, or class definition. The template declaration takes one or more parameters, which can be values, types, or class templates.
In most cases, you can use a template simply by naming the template and providing arguments for the template parameters: constant expressions, type specifiers, or template references. This is known as instantiating the template. You can instantiate a template at its point of use, or declare a separate instantiation as a class or function declaration.
A template lets you define a class or function once for a wide range of template arguments, but sometimes you need to customize a template for particular arguments. This is known as specializing the template.
Writing a template is more difficult than writing a non-template class or function. The template can be instantiated in almost any context, and the context can affect how the template definition is interpreted. Name lookup rules are more complicated for templates than for non-templates.
The following sections cover these topics in depth. Example 8-1 shows several different kinds of templates and their uses.
Example 8-1: Declaring and using templates.
#include <cmath> #include <complex> #include <iostream> #include <ostream> // Template declaration of point. template<typename T> class point { public: typedef T value_type; point(const T& x, const T& y) : x_(x), y_(y) {} point() : x_(T()), y_(T()) {} T x() const { return x_; } T& x() { return x_; } void x(const T& new_x) { x_ = new_x; } T y() const { return y_; } T& y() { return y_; } void y(const T& new_y) { y_ = new_y; } private: T x_, y_; }; // instantiate point<> typedef point<std::complex<double> > strange; strange s(std::complex<double>(1, 2), std::complex<double>(3, 4)); // Specialize point<int> to use call-by-value instead // of const references. template<> class point<int> { public: typedef int value_type; point(int x, int y) : x_(x), y_(y) {} point() : x_(0), y_(0) {} int x() const { return x_; } int& x() { return x_; } void x(int new_x) { x_ = new_x; } int y() const { return y_; } int& y() { return y_; } void y(int new_y) { y_ = new_y; } private: int x_, y_; }; // Instance of the specialized point<int>. point<int> p(42, 0); // Instance of the general point<>, using long as // the template argument. point<long> p(42, 0); // function template template<typename T> T abs(T x) { return x < 0 ? -x : x; } namespace { // Explicit instantiation. const int abs_char_min1 = abs<int>(CHAR_MIN); // Implicit instantiation. const int abs_char_min2 = abs(CHAR_MIN); } // Overload abs() with another function template. template<typename floatT, typename T> floatT abs(const point<T>& p) { return std::sqrt(static_cast<floatT>(p.x()*p.x() + p.y()*p.y())); } int main() { point<double> p; // Call instance of function template. Compiler deduces // second template argument (double) from the type of p. double x = abs<long double>(p); std::cout << x << '\n'; // prints 0 std::cout << abs_char_min1 << '\n'; }
A template declaration begins with a template header, followed by a function or class declaration or definition. Template declarations can appear only at namespace or class scope. The template name must be unique in its scope (except for overloaded functions).
The template header starts with the template
keyword, followed by the template parameters, enclosed in angle brackets (<>
). Multiple parameters are separated by commas. The syntax is as follows:
template <
parameter-list>
declaration
A template parameter can represent a value, a type, or a class template. Similar to function parameters, a template parameter has an optional name and an optional default argument.
If the default argument is present, it is preceded by an equal sign. Only the right-most parameters can have default arguments. If any parameter has a default argument, all parameters to its right must also have default arguments. Function templates cannot have default arguments. (Use overloading instead.) If a class template has member definitions that are outside the class definition, only the class template takes default arguments; the individual member definitions do not. Example 8-2 show valid and invalid member definitions.
Example 8-2: Defining members of a class template.
// okay: default argument for template parameter A. template<typename T, typename A = std::allocator<T> > class hashset { bool empty() const; size_t size() const; ... }; // error: do not use default argument here template<typename T, typename A = std::allocator<T> > bool hashset<T,A>::empty() { return size() == 0; } // okay template<typename T, typename A> size_t hashset<T,A>::size() { ... }
Each template header defines the template name and template parameters. The scope of a parameter name extends to the end of the class or function declaration or definition. A parameter name can be used in subsequent template parameters (such as std::allocator<T>
in Example 8-2). The template parameter name must be unique in the template declaration, and cannot be redeclared in its scope. If a class template has separate definitions for its members, each member definition is free to use different names for the template parameters. See Member Templates, later in this chapter for more information.
There are three kinds of template parameters:
type-specifiers declarator
type-specifiers declarator =
expr
The type must be an integral, enumeration, pointer, or reference type. When the template is instantiated, the argument must be a constant integral or enumeration expression, the address of a named object or function with external linkage, or the address of a member. For example,
template<unsigned SIZE> struct name { // ... unsigned size() const { return SIZE; } private: char name_[SIZE+1]; }; name<100> n;
Note that a string literal is an unnamed object with internal linkage, so you cannot use it as a template argument. For example,
template<const char* STR> void print(const char* s = STR); print<"default">(); // error const char def[] = "default"; print<def>(); // okay
typename
, followed by the optional parameter name:typename
identifier
typename
identifier=
type
Theclass
keyword can be used in place oftypename
and has the same meaning in this context. (A useful convention is to useclass
when the argument must be a class, andtypename
when the argument can be any type.) When the template is instantiated, the argument must be a type (that is, a list of type specifiers, with optional pointer and reference operators). For example,
template<class T> size(const T& x) { return x.size(); } template<typename T> struct point { T x, y; }; point<unsigned long int> pt;
Iftypename
is followed by a qualified type name (instead of a plain identifier), it declares a value parameter of that type. (For more information about this use oftypename
, see Name Lookup, later in this chapter.) For example,
template<typename list<int>::value_type value> int silly() { return value; }
template
<
parameter-list>
class
identifier
template
<
parameter-list>
class
identifier=
template-id
When the template is instantiated the argument must be a class template. For example,
// Erase all occurrences of item from a sequence container. template<template<typename T, typename A> class C, typename T, typename A> void erase(C<T,A>& c, const T& item) { c.erase(std::remove(c.begin(), c.end(), item), c.end()); } ... list<int> l; ... erase(l, 42);
To use a template declaration, you must create an instance of the template, either explicitly (by naming the template, and enclosing a list of template arguments in angle brackets) or implicitly (by letting the compiler deduce the template arguments from context). In either case, the compiler must know about the template declaration before the template is used. Typically, the template is declared in an #include
file or header. The header declares the template and possibly provides the definitions of all the function templates and members of class templates. See Compiling Templates, at the end of this chapter, for details about the files that declare and define templates.
A template instance must provide an argument for each template parameter. If a class template has fewer arguments than parameters, the remaining parameters must have default arguments, which are used for the template instance.
If a function template has fewer arguments than parameters, the remaining arguments are deduced from the context of the function call, as explained in the next section. Also see Instantiation, later in this chapter, for more information.
A function template defines a pattern for any number of functions whose definitions depend on the template parameters. You can overload a function template with a non-template function or with other function templates. You can even have a function template and a non-template function with the same name and parameters.
Function templates are used throughout the standard library, such as getline()
, and especially in the standard algorithms, such as copy()
and accumulate()
.
Declare a function template using a template declaration header followed by a function declaration or definition. Default template arguments are not allowed in a function template declaration or definition. For example,
template<unsigned N, typename T> T round(T x); template<typename T> T min(T a, T b) { return a < b ? a : b; }
To use a function template, you must specify a template instance by providing arguments for each template parameter. You can do this explicitly, by listing arguments inside angle brackets. For example,
int x = min<int>(10, 20);
More common, though, is to let the compiler deduce the template argument types from the types of the function arguments. For example,
int x = min(10, 20); // calls min<int>()
See the section, Deducing Argument Types, later in this chapter, for details.
The function signature includes the template parameters even if those parameters are never used in the function's return type or parameter types. Thus, separate template specializations denote distinct functions. For example,
template<typename T> void func(); template<int i> void func();
Typically, such functions are instantiated in separate files, or else the compiler cannot resolve the overloaded names.
The function signature also includes expressions that use any of the template parameters. For example,
template<int x> struct demo {}; template<typename T> void func(demo<sizeof(T)>); template<int i> void func(demo<i+42>);
You can declare the same template multiple times, using expressions that differ only by the template parameter names. Even if the declarations are in separate files, the compiler and linker ensure that the program ends up with a single copy of each template instance.
If the expressions differ by more than just the parameter names, they result in distinct functions. If the argument expressions result in the same value, the program is invalid, although the compiler is not required to issue an error message. For example,
template<int i> void func(demo<i+42>); template<int x> void func(demo<x+42>); // okay template<int i> void func(demo<i+41>); // okay template<int i> void func(demo<i+40+2>); // error
If you follow standard procedure of declaring templates in header files, and using those headers in the source files where they are needed, this rule should not affect you because every source file will see the same template declaration. Problems arise only if you are writing separate declarations for what you intend to be the same template. In that case, the compiler uses this simple rule to try to ensure that the program ends up with a single copy of functions that are meant to be the same, but distinct copies of those that are meant to be distinct.
Most uses of function templates do not explicitly specify all of the template arguments, letting the compiler deduce the template arguments from the function call. You can provide explicit arguments for the leftmost template parameters, leaving the rightmost parameters for the compiler to deduce. (Remember that default template arguments are not permitted in a function template.) If you omit all the template arguments, you can choose to omit the angle brackets, too.
In the following discussion, take care to distinguish between the parameters and arguments of a template and those of a function. Argument type deduction is the process of determining the template arguments, given a function call or other use of a function (such as taking the address of a function).
The basic principle is that the function call has a list of function arguments, where each function argument has a type. The function argument types must be matched to function parameter types; in the case of a function template, the function parameter types depend on the template parameters. The ultimate goal, therefore, is to determine the appropriate template arguments that can match the function parameter types with the given function argument types.
Usually, the template parameters are type or template parameters, but in a few cases, value parameters can also be used (as the argument for another template, or as the size of an array).
The function parameter type can depend on the template parameter in the following ways, where the template parameter is v for a value parameter, T for a type parameter, or TT for a template parameter:
*
, T&
, const
T, volatile
T, const
volatile
TTypes can be composed from any of the forms listed above. Thus, type deduction can occur for a struct where the types of its members are also deduced, or a function's return type and parameter types can be deduced.
A single function argument can result in the deduction of multiple template parameters, or multiple function arguments might depend on a single template parameter.
If the compiler cannot deduce all the arguments, you must provide explicit arguments. Example 8-3 shows several different ways the compiler can deduce template arguments.
Example 8-3: Deducing template arguments.
#include <algorithm> #include <cstddef> #include <iostream> #include <list> #include <ostream> #include <set> // Simple function template. Easy to deduce T. template<typename T> T min(T a, T b) { return a < b ? a : b; } // More complicated function template. Easy to deduce SRC, // but impossible to deduce DST. template<typename DST, typename SRC> DST cast(SRC s) { return s; } // Deduce the row size of a 2D array. template<typename T, std::size_t SIZE> std::size_t dim2(T [][SIZE]) { return SIZE; } template<typename T, std::size_t SIZE> struct array { T value[SIZE]; }; // Overload the function size() to return the number of // elements in an array<> or in a standard container. template<typename T, std::size_t SIZE> std::size_t size(const array<T,SIZE>&) { return SIZE; } template<template<typename T, typename A> class container, typename T, typename A> typename container<T,A>::size_type size(container<T,A>& c) { return c.size(); } template<template<typename T, typename C, typename A> class container, typename T, typename C, typename A> typename container<T,C,A>::size_type size(container<T,C,A>& c) { return c.size(); } int main() { min(10, 100); // min<int> min(10, 1000L); // error: T cannot be int and long array<int,100> data1; // deduce T=int and SIZE=100 from type of data1 std::cout << size(data1) << '\n'; int y[10][20]; std::cout << dim2(y) << '\n'; // prints 20 std::list<int> l; l.push_back(10); l.push_back(20); l.push_back(10); std::cout << size(l) << '\n'; // prints 3 std::set<char> s; const char text[] = "hello"; std::copy(text, text+5, std::inserter(s, s.begin())); std::cout << size(s) << '\n'; // prints 4 // Can deduce SRC=int, but not return type, so // explicitly set DST=char. char c = cast<char>(42); }
Function templates can be overloaded with other function templates and with non-template functions. Templates introduce some complexities, though, beyond those of ordinary overloading (as covered in Chapter 6).
As with any function call, the compiler collects a list of functions that are candidates for overload resolution. If the function name matches that of function templates, the templates that are suitable matches are added to the list of candidate functions. Each function template requires template arguments for all of its parameters, deduced or explicit. If template arguments cannot be deduced, that template is not added to the list of candidates. Thus, each template contributes at most one function to the list of overload candidates. The best match is then chosen from the list of template and non-template functions according to the normal rules of overload resolution: non-template functions are preferred over template functions, and more specialized template functions are preferred over less specialized template functions. See Chapter 6 for more details on overload resolution.
Example 8-4 shows examples of how templates affect overload resolution.
Example 8-4: Overloading functions and function templates.
#include <cctype> #include <complex> #include <iomanip> #include <iostream> #include <ostream> #include <string> #include <vector> template<typename T> void print(const T& x) { std::cout << x; } void print(char c) { if (std::isprint(c)) std::cout << c; else std::cout << std::hex << std::setfill('0') << "'\\x" << std::setw(2) << int(c) << '\'' << std::dec; } template<> void print(const long& x) { std::cout << x << 'L'; } int main() { print(42L); // calls print<long> specialization print('x'); // calls print(char) overload print('\0') // calls print(char) overload print(42); // calls print<int> print(std::string("y")); // calls print<string> print(std::vector<int>()); // error print(); // error }
You can create operator templates in the same manner as function templates. The usual rules for deducing argument types apply, as described earlier in this section. If you want to specify the arguments explicitly, you can use the operator
keyword, for example,
template<typename T> bigint operator+(const bigint& x, const T& y); bigint a; a = a + 42; // argument type deduction a = operator+<long>(a, 42); // explicit argument type
When using operator<
, take care to leave a space between the operator symbol and the angle brackets that surround the template argument, or else the compiler will think you are using the left-shift operator.
Type conversion operators can use ordinary type cast syntax, but if you insist on using an explicit operator
keyword, you cannot use the usual syntax for specifying the template argument. Instead, the target type specifies the template argument type. For example,
struct demo { template<typename T> operator T*() const { return 0; } }; demo d; void* p = d.operator int*(); char* c = d; // d.operator char*();
A class template defines a pattern for any number of classes whose definitions depend on the template parameters. Every member function of the class is a function template with the same parameters as the class template.
Class templates are used throughout the standard library, for containers (e.g., list<>
, map<>
), complex numbers (complex<>
), and even strings (basic_string<>
) and I/O (e.g., basic_istream<>
).
The basic form of a class template is a template declaration header followed by a class declaration or definition. For example,
template<typename T> struct point { T x, y; };
To use the class template, supply an argument for each template parameter. Use the template reference the way you would a class name. For example,
point<int> pt = { 42, 10 }; typedef point<double> dpoint; dpoint dp = { 3.14159, 2.71828 };
In member definitions that are separate from the class definition, you must declare the template using the same template parameter types, in the same order, but without any default arguments. (You can change the names of the template parameters, but be careful of name collisions with base classes and their members. See Name Lookup, later in this chapter, for more information.) Declare the member definitions the way you would any other definition, except that the class name is a template name (with arguments), and the definition is preceded by a template declaration header.
Within the class definition, the bare class name is short-hand for the full template name with arguments. Thus, you can use the bare identifier as the constructor name, after the tilde in a destructor name, in parameter lists, and so on. Outside the class definition, you must supply template arguments when using the template name. You can also use the template name with different arguments to specify a different template instance. For example,
template<typename T> struct point { point(T x, T y); // or point<T>(T x, T y); ~point<T>(); // or ~point(); template<typename T> point(const point<U>& that); ... };
Example 8-5 shows a class template with several member template definitions.
Example 8-5: Defining class templates.
template<typename T, typename U = int> class demo { public: demo(T t, U u); ~demo(); static T data; class inner; // inner is a plain class, not a template }; template<typename T, typename U> demo<T,U>::demo(T, U) // or demo<T,U>::demo<T,U>(T, U) {} template<typename T, typename U> demo<T,U>::~demo<T,U>() // or demo<T,U>::~demo() {} template<typename U, typename T> // allowed, but confusing U demo<U,T>::data; template<typename T, typename U> class demo<T,U>::inner { public: inner(T, demo<T,U>& outer); private: demo<T,U>& outer; }; // The class name is "demo<T,U>::inner", and the constructor // name is "inner". template<typename T, typename U> demo<T,U>::inner::inner(T, demo<T,U>& outer) : outer(outer) {}
The template defines only the pattern; the template must be instantiated to declare or define a class. See the sections on Specialization and Instantiation, later in this chapter, for more information.
A member template is a template inside a class template or non-template class. A member template has its own template declaration header, with its own template parameters. A member template can be a member function, data member, or nested type. Member function templates have the following restrictions:
Conversion functions have addition rules because they cannot be instantiated using the normal template instantiation or specialization syntax.
using
declaration to refer to a type conversion template in a base class.template<typename T> struct silly { template<typename U> operator U*() { return 0; } }; ... silly<int> s, t(42); if (static_cast<void*>(s)) std::cout << "not null\n";
A class can have a non-template member function with the same name as a member function template. The non-template function is called in preference to the function template unless you explicitly call the template. For example,
template<typename T> struct demo { template<typename U> void func(U); void func(int); }; demo<int> d; d.func(42); // calls func(int) d.func<int>(42); // calls func<>
A friend can be a template, a specialization of a template, or a non-template function or class. A friend declaration cannot be a partial specialization. If the friend is a template, all instances and specializations of the template are friends. If the class granting friendship is a template, all instances and specializations of the template grant friendship. A specialization of a template can be a friend, in which case, only that specialization is granted friendship. For example,
template<typename T> class buddy {}; template<typename T> class special {}; class demo { // All specializations of buddy<> are friends. template<typename T> friend class buddy; // special<int> is a friend, but not special<char>, etc. friend class special<int>; };
When you use the containing class template as a function parameter, you probably want friend functions to be templates, too. For example,
template<typename T> class outer { friend void func1(outer& o); // wrong template<typename U> friend void func2(outer<U>& o); // right };
A friend function can be defined in a class template, in which case, every instance of the class template defines the function. The function definition is compiled even if it is not used.
You cannot declare a friend template in a local class.
If a friend declaration is a specialization of a function template, you cannot specify any default function arguments, and you cannot use the inline
specifier. These items can be used in the original function template declaration, though.
All the rules for function templates apply to friend function templates. See Function Templates, earlier in this chapter, for details. Example 8-6 shows examples of friend declarations and templates.
Example 8-6: Declaring friend templates and friends of templates.
#include <algorithm> #include <iostream> #include <iterator> #include <ostream> template<typename T, size_t SIZE> class array { public: typedef T value_type; class iterator_base : std::iterator<std::random_access_iterator_tag, T> { public: typedef T value_type; typedef size_t size_type; typedef ptrdiff_t distance_type; friend inline bool operator==(const iterator_base& x, const iterator_base& y) { return x.ptr_ == y.ptr_; } friend inline bool operator!=(const iterator_base& x, const iterator_base& y) { return ! (x == y); } friend inline ptrdiff_t operator-(const iterator_base& x, const iterator_base& y) { return x.ptr_ - y.ptr_; } protected: iterator_base(const iterator_base& that) : ptr_(that.ptr_) {} iterator_base(T* ptr) : ptr_(ptr) {} iterator_base(const array& a, size_t i) : ptr_(a.data_ + i) {} T* ptr_; }; friend class iterator_base; class iterator : public iterator_base { public: iterator(const iterator& that) : iterator_base(that) {} T& operator*() const { return *this->ptr_; } iterator& operator++() { ++this->ptr_; return *this; } T* operator->() const { return this->ptr_; } private: friend class array; iterator(const array& a, size_t i = 0) : iterator_base(a, i) {} iterator(T* ptr) : iterator_base(ptr) {} friend inline iterator operator+(iterator iter, int off) { return iterator(iter.ptr_ + off); } }; array() : data_(new T[SIZE]) {} array(const array& that); ~array() { delete[] data_; } iterator begin() { return iterator(*this); } const_iterator begin() const { return iterator(*this); } iterator end(); const_iterator end() const; T& operator[](std::size_t i); T operator[](std::size_t i); template<typename U, size_t USIZE> friend void swap(array<U,USIZE>& a, array<U,USIZE>& b); private: T* data_; }; template<typename T, size_t SIZE> void swap(array<T,SIZE>& a, array<T,SIZE>& b) { T* tmp = a.data_; a.data_ = b.data_; b.data_ = tmp; }
A template declares a set of functions or classes from a single declaration, which is a powerful tool for writing code once and having it work in a multitude of situations. A single, one-size-fits-all approach is not always appropriate, though. You can therefore specialize a template for specific values of one or more template arguments and provide a completely different definition for that special case.
You can specialize a class template, a function template, or a member of a class template. Declare a specialization with an empty set of template parameters in the template declaration, followed by a declaration that provides all the template arguments:
template<>
declaration
Specializations must be declared in the same namespace as the primary template. You can also have a partial specialization, which provides some of the template arguments; partial specializations are the subject of the next section.
To specialize a function template, the function return type and parameter types must match the primary template declaration. You can list the template arguments explicitly (enclosed in angle brackets and separated by commas), or omit them and let the compiler deduce the arguments. (See Deducing Argument Types, earlier in this chapter, for details.)
If you need to change the function parameters, you can overload the function, but that requires an entirely new function or function template declaration, not a specialization. Example 8-7 shows several specializations of a function template.
Example 8-7: Specializing a function template.
// primary template declaration template<typename T> T root(T x, T y) { return pow(x, 1.0/y); } // specialization where T is deduced to be long template<> long root(long x, long y) { if (y == 2) return sqrt((long double)x); else return pow((long double)x, 1.0l/y); } // specialization for explicit T=int template<> int root<int>(int x, int y) { if (y == 2) return sqrt(double(x)); else return pow(double(x), 1.0/y); } // Overload with a different function template, such // as valarray<T>. template<template<typename T> class C, typename T> C<T> root(const C<T>& x, const C<T>& y) { return pow(x, C<T>(1.0)/y); }
A specialization of a class template requires an entirely new class declaration or definition. For a class definition, you must define all the members of the specialization. If you want to specialize only some members, you can specialize just those members instead of specializing the entire class.
In a specialization of a static data member, you must supply an explicit initializer to define the data member. Without an initializer, the member template declaration is just a declaration of the data member, not a definition. For example,
template<typename T> struct demo { static T data; }; template<> int demo<int>::data; // declaration template<> int demo<int>::data = 42; // definition
Declare specializations before the template is used. See Instantiations, later in this chapter, for information on how templates are "used." A program must have a single specialization for a given set of template arguments: either an explicit specialization, or an instantiation of the primary template.
Example 8-8 shows some specializations of a class template, type_traits
, which exposes a few attributes, or traits, of a type. The primary template describes the default traits, and the template is specialized for specific types, such as int
. (See Chapter 9 for more information about traits.)
Example 8-8: Specializing a class template.
#include <iostream> #include <ostream> #include <sstream> #include <string> #include <typeinfo> template<typename T> struct type_traits { typedef T base_type; enum { is_fundamental = 0 }; enum { is_integer = 0 }; enum { is_float = 0 }; enum { is_pointer = 0 }; enum { is_reference = 0 }; static std::string to_string(const T&); }; template<typename T> std::string type_traits<T>::to_string(const T& x) { return typeid(x).name(); } // Specialize entire class for each fundamental type. template<> struct type_traits<int> { typedef int base_type; enum { is_fundamental = 1 }; enum { is_integer = 1 }; enum { is_float = 0 }; enum { is_pointer = 0 }; enum { is_reference = 0 }; static std::string to_string(const int& x) { std::ostringstream out; out << x; return out.str(); } }; struct point { int x, y; }; // Specialize only the to_string() member function // for type point. template<> std::string type_traits<point>::to_string(const point& p) { std::ostringstream out; out << '(' << p.x << ',' << p.y << ')'; return out.str(); } int main() { using std::cout; cout << type_traits<point>::is_fundamental << '\n'; cout << type_traits<point>::is_pointer << '\n'; cout << type_traits<point>::to_string(point()) << '\n'; cout << type_traits<int>::is_fundamental << '\n'; cout << type_traits<int>::is_pointer << '\n'; cout << type_traits<int>::is_integer << '\n'; cout << type_traits<int>::to_string(42) << '\n'; }
You can choose to specialize only some of the parameters of a class template. This is known as partial specialization. Note that function templates cannot be partially specialized; use overloading to achieve the same effect.
A partial specialization is declared with a template header that contains one or more template parameters. (With no template parameters, it is a complete specialization, not a partial specialization; see the previous section for details.) The class declaration that follows the template header includes a subset of the arguments needed for the class template. The template arguments can depend on the specialized template parameters.
The partial specialization of a class is a distinct template and must provide all the members. You cannot partially specialize a member of a class template, only the entire class template.
Example 8-9 shows partial specializations of the type_traits
template from Example 8-8.
Example 8-9: Partially specializing a class template.
// Specialize for all pointer types. template<typename T> struct type_traits<T*> { typedef T base_type; enum { is_fundamental = type_traits<T>::is_fundamental }; enum { is_integer = 0 }; enum { is_float = 0 }; enum { is_pointer = 1 }; enum { is_reference = 0 }; static std::string to_string(const T& x) { std::ostringstream out; out << x; return out.str(); } }; // Specialize for const types, so base_type refers to // the non-const type. template<typename T> struct type_traits<const T> { typedef T base_type; typedef type_traits<base_type> base_type_traits; enum { is_fundamental = base_type_traits::is_fundamental }; enum { is_integer = base_type_traits::is_integer }; enum { is_float = base_type_traits::is_float }; enum { is_pointer = base_type_traits::is_pointer }; enum { is_reference = base_type_traits::is_reference }; static std::string to_string(const base_type& x) { return type_traits<base_type>::to_string(x); } };
Template declarations and specializations describe the form that a class or function can take, but does not create any actual classes or functions. To do that, you must instantiate the template.
Most often, you implicitly instantiate a template by using it in a function call, declaration, or similar contexts. The template instance requires a template argument for each template parameter. The arguments can be explicit (enclosed in angle brackets and separated by commas) or implicit (default arguments for class templates or deduced arguments for function templates. For example,
template<typename T> T plus(T a, T b) { return a + b; } template<typename T = int> struct wrapper { T value; }; int x = plus(10, 20); // instantiate plus<int> wrapper<double> x; // instantiate wrapper<double> wrapper w; // instantiate wrapper<int>
A class member expression (.
or ->
operator) that names a specialized instance of a member template must use the template
keyword before the member name. Similarly, a qualified name of a member template must use template
before the member name. Otherwise, the <
symbol that introduces the template argument list is interpreted as the less-than operator. For example,
class bigfloat { template<unsigned N> double round(); template<unsigned N> static double round(double); }; bigfloat f; std::cout << f.template round<2>() << '\n'; std::cout << bigfloat::template round<8>(PI) << '\n';
Instantiating a class template does not instantiate all the members. Members of a class template are instantiated only when they are needed. Each instance of a class template has its own, separate copy of the class's static data members.
If a class template has partial specializations, the compiler must choose which specialization to instantiate. It starts by choosing which partial specializations match the template arguments. A partial specialization matches if the specialized template arguments can be deduced from the actual template arguments. (See Deducing Template Arguments, earlier in this chapter.)
If no specializations match, the compiler instantiates the primary template. If one specialization matches, that one is instantiated. Otherwise, the best match is chosen, where "best" means most specialized. It is an error if two or more specializations are tied for best. Template specialization A is at least as specialized as template specialization B if the parameters of A can be deduced from B's template parameter list. No implicit conversion takes place when comparing specializations. For example,
template<typename T, typename U, int N> class demo {}; template<typename T, int N> class demo<T, int, N> {}; template<typename T> class demo<T, T, 0> {}; demo<int, char, 1> w; // primary template demo<char, int, 10> x; // first specialization demo<char, char, 0> y; // last specialization demo<char, char, 1> z; // primary template
In addition to the obvious points where a function or class is needed (e.g., calling the function, taking the address of a function, declaring an object whose type is a class, casting an object to a class), there are more subtle points of instantiation. For example, a template can be instantiated if it used in the value of a default function argument, and the default argument is needed in a function call.
A function template is needed if an instance of the template is needed for overload resolution. If a class template participates in function overloading (that is, as part of a function parameter type), but the template does not need to be instantiated in order to resolve the overload (say, because the function parameter is a pointer or reference to the class template), the class template may or may not be instantiated. The standard leaves this up to the implementation.
If a template declaration contains an error, the compiler can diagnose the error when it compiles the template or when it instantiates the template. If a program does not instantiate the template, you might find that you can compile the program successfully using one compiler (which reports errors only when a template is instantiated), but not using a different compiler (which reports errors when a template is declared).
You can explicitly instantiate a template. Use a bare template
keyword, followed by a declaration that supplies the template arguments for the template. For example,
template long int plus<long int>(long int a, long int b); template short plus(short a, short b); // deduce plus<short> template struct wrapper<unsigned const char*>;
The class, function, or member template must be defined before it can be instantiated. If you are instantiating a specialization, the template specialization must appear earlier in the source than the instantiation. It is an error to specialize a template after it has been instantiated for the same template arguments. Example 8-10 illustrates template instantiation.
Example 8-10: Instantiating templates.
#include <iomanip> #include <iostream> #include <ostream> // Function template template<typename T> T sqr(T x) { return x * x; } // Class template template<typename T> class circle { public: circle(T r) : radius_(r) {} // sqr<> is instantiated when circle<> is instantiated. T area() const { return pi * sqr(radius_); } T radius() const { return radius_; } private: T radius_; }; // Function template template<typename T> void print(T obj) { std::cout << obj << '\n'; } // Overload the function template with another template. template<typename T> void print(const circle<T>& c) { std::cout << "circle(" << c.radius() << ")\n"; } template int sqr<int>(int); // explicit instantiation // Explicit instantiation of circle<double>, and // implicit instantiation of sqr<double> template class circle<double>; // Error: after instantiation of sqr<double>, // illegal to specialize it. template<> double sqr(double x) { return x * x; }, int main() { using namespace std; // No circle<> instance needed yet, even to resolve // overloaded print function. print(42); for (int i = 0; i < 10; ++i) // implicit instantiation of sqr<int> cout << setw(2) << i << setw(4) << sqr(i) << '\n'; // instantiation of circle<float> and therefore sqr<float> circle<float> unit(1.0f); // implicit instantiation of print<circle<float> > print(unit); }
Templates introduce a new wrinkle to name lookup. (See Chapter 3 for the non-template rules of name lookup.) When compiling a template, the compiler distinguishes between names that depend on the template parameters (called dependent names) and those that do not (non-dependent names). Non-dependent names are looked up normally, where the template is declared. Dependent names, on the other hand, must wait until the template is instantiated, when the template arguments are bound to the parameters. Only then can the compiler know what those names truly mean.
A dependent name is a name that can have different meanings in different template instantiations. In particular, a function is dependent if any of its arguments are type-dependent. An operator has a dependent name if any of its operands are type-dependent.
This section describes dependent names and the following section describes what the compiler does with them.
A dependent type is a type that that can change meaning if a template parameter changes. More precisely, the following are dependent types:
const
or volatile
qualified version of a dependent type, orA type-dependent expression is one that has a dependent type. It can be any of the following:
this
, if the class type is dependent,new
expression, creating an object of dependent type, orsizeof
or typeid
expression,.
or ->
operators),throw
expression, ordelete
expression.A constant expression can also be value-dependent if its type is dependent or if any subexpression is value-dependent. An identifier is value-dependent in the following cases:
A sizeof
expression is value-dependent only if its operand is type-dependent. A cast expression is dependent if its operand is a dependent expression. Example 8-11 shows a variety of dependent types and expressions.
Example 8-11: Dependent names.
template<typename T> struct base { typedef T value_type; // value_type is dependent void func(T*); // func is dependent void proc(int); // proc is non-dependent class inner { // inner is dependent int x; // x is non-dependent }; template<unsigned N> class data { // data is dependent int array[N]; // array is dependent }; class demo : inner { // demo is dependent char y[sizeof(T)]; // y is dependent }; }; int main() { base<int> b; }
When writing a template definition, you should use qualified names as much as possible. Use member expressions to refer to data members and member functions (e.g., this->data
). If a name is unqualified, the name lookup rules are different from the rules for non-templates.
The compiler looks up unqualified, non-dependent names at the point where the template is defined. Dependent base classes are not searched (because the compiler does not know anything about the instantiation base class at the point of declaration). This can give rise to surprising results. For example,
template<typename T> struct base { double x; }; int x; template<typename T> struct derived : base<T> { int get_x() const { return x; } // returns ::x };
Dependent names are looked up in the declaration context and in the instantiation context. In particular, when performing argument-dependent name lookup (Chapter 3), the compiler searches the declaration and instantiation namespaces for the function argument types.
The instantiation context, roughly speaking, is the innermost namespace scope that encloses the template instantiation. Thus, the instantiation context never includes local declarations.
A function template can have multiple instantiation points for the same template arguments in a single source file. A class template can have multiple instantiations for the same template arguments in multiple source files. If the different contexts for the different instantiations result in different definitions of the templates, the behavior is undefined. The best way to avoid this undefined behavior is to avoid using unqualified dependent names.
Example 8-12 shows several ways dependent name lookup affects a template. In particular, notice that iterator_base
can refer to its members without qualification or member expressions. The derived classes, such as iterator
, however must use this->
or qualify the member name with the base class because the base class is not searched for unqualified names. The print
member function is also interesting. It prints the array with an ostream_
i
terator
, which calls operator<<
to print each element. The name operator<<
is dependent, so it is not looked up when the template is declared, but when the template is instantiated. At that time, the compiler knows the template argument, big::integer
, so it also knows to search the big
namespace for the right overloaded operator<<
.
Example 8-12: Resolving dependent names.
#include <algorithm> #include <iostream> #include <iterator> #include <ostream> #include <stdexcept> template<unsigned SIZE, typename T> class array { template<unsigned SZ, typename U> friend class array<SZ,U>::iterator; template<unsigned SZ, typename U> friend class array<SZ,U>::const_iterator; class iterator_base { public: iterator_base& operator++() { ++ptr; return *this; } T operator*() const { check(); return *ptr; } protected: iterator_base(T* s, T* p) : start(s), ptr(p) {} void check() const { if (ptr >= start + SIZE) throw std::out_of_range("iterator out of range"); } T* ptr; T* start; }; public: array(): data(new T[SIZE]) {} class iterator : public iterator_base, public std::iterator<std::random_access_iterator_tag,T> { public: iterator(T* s, T* p) : iterator_base(s, p) {} operator const_iterator() const { return const_iterator(this->start, this->ptr); } T& operator*() { iterator_base::check(); return *this->ptr; } }; iterator begin() { return iterator(data, data); } iterator end() { return iterator(data, data + SIZE); } template<typename charT, typename traits> void print(std::basic_ostream<charT,traits>& out) const { std::copy(begin(), end(), std::ostream_iterator<T>(out)); } private: T* data; }; namespace big { class integer { public: integer(int x = 0) : x_(x) {} operator int() const { return x_; } private: int x_; }; template<typename charT, typename traits> std::basic_ostream<charT,traits>& operator<<(std::basic_ostream<charT,traits>& out, const integer& i) { out << int(i); return out; } } int main() { const array<10, big::integer> a; a.print(std::cout); }
When parsing a template definition, the compiler must know which names are types and which are objects or functions. Unqualified names are resolved using the normal name lookup rules, described earlier in this section. Qualified dependent names are resolved according to a simple rule: if the name is prefaced with typename
, it is a type; otherwise it not a type.
Use typename
only in template declarations and definitions, and only with qualified names. Although typename
is meant to be used with dependent names, you can use it with non-dependent names, too. Example 8-13 shows a typical use of typename
.
Example 8-13: Using typename in a template definition.
// Erase all items from an associative container for which // a predicate returns true. template<typename C, typename Pred> void erase_if(C& c, Pred pred) { // Need typename because iterator is qualified. for (typename C::iterator it = c.begin(); it != c.end();) if (pred(*it)) c.erase(*it++); else ++it; }
The template syntax permits recursion, selection, and computation. In other words, it is a full-fledged programming language. The full scope and power of programming with templates is beyond the scope of this book. This section presents some tips for starting your own exploration of this exciting field. Appendix ### tells you about some interesting projects in this area.
For example, suppose you want to write a function to round off floating point values to a fixed number of decimal places. You decide to write the function by multiplying by a power of ten, rounding off to an integer, and dividing by the same power of ten. You can hardcode the amount, say, 100, in the routine, but you prefer to use a template, where a template parameter specifies the number of digits. To avoid computing the same power of ten every time the function is called, you decide to use template programming to compute the constant at compile time.
The ipower<>
template in Example 8-14 uses recursion to compute the power of any integer raised to any non-negative integer value. Three base cases for the recursion are defined as specializations: raising any value to the zeroth power is always one; raising zero to any power is always zero; and raising zero to the zeroth power is undefined. (An exercise left to the reader is to define ipower
more efficiently.)
Example 8-14: Computing at compile-time
template<int x, unsigned y> struct ipower { enum { value = x * ipower<x, y-1>::value }; }; template<int x> struct ipower<x, 0> { enum { value = 1 }; }; template<unsigned y> struct ipower<0, y> { enum { value = 0 }; }; template<> struct ipower<0, 0> {}; // Round off a floating point value to a fixed number // of digits. template<unsigned N, typename T> T round(T x) { if (x < 0.0) return floor((x * ipower<10,N>::value - 0.5) / ipower<10,N>::value); else return floor((x * ipower<10,N>::value - 0.5) / ipower<10,N>::value); }
A template declaration can begin with the export
keyword to export the template. An exported template lets you define a function template or define all the members of a class template in a separate source file.
As I write this, exactly one compiler supports export
. (See Appendix ###.) Major compiler vendors are finally moving toward full conformance with the standard, including export
. Nonetheless, I doubt export
will see widespread support, at least for the next few years.
If portability is important, you should not use export
.
Without export
, the typical usage is to place a template's declaration and all associated definitions in a single source file, say, template.h, and #include
that file anywhere the template is needed. The compiler implicitly instantiates the template when and where it is needed. If the template is instantiated with the same template arguments in multiple source files, the compiler and linker make sure only one copy ends up in the program.
A few compilers work slightly differently. The declaration resides in a header file, say, template.h, and the definitions of the non-inline template functions reside in a source file, say, template.cc. The compiler automatically locates the definitions file from the name of the declaration file. In this scenario, the compiler keeps track of which instantiations it needs and compiles them only once.
A hybrid approach requires all the definitions in the header file, but puts off compiling the instantiations. The first time a file is compiled, the compiler records which instantiations are required. When the program is linked, all the required instantiations are compiled and linked with the rest of the program.
Consult your compiler's documentation to learn how it handles templates. When writing a template that can be used by multiple compilers, a common technique is to put the declaration in a header file, say, template.h, and the definitions in another file, say, template.cc, and at the end of template.h, use conditional directives to #include
"template.cc"
for those compilers where it is needed. Example 8-15 shows this common pattern.
Example 8-15: Declaring and defining a template.
// point.h #ifndef POINT_H #define POINT_H template<typename T> class point { public: point(T a, T b); point(); T x() const { return x_; } T y() const { return y_; } private: T x_, y_; } #ifdef NEED_TEMPLATE_DEFINITIONS #include "point.cc" #endif #endif // POINT_H // point.cc #include "point.h" template<typename T> point<T>::point(T a, T b) : x_(a), y_(b) {} template<typename T> point<T>::point() : x_(T()), y_(T()) {}
If your compiler supports export
, you can put the declaration in a header file, and declare the declaration with export
. Put the definitions of the non-inline functions in a separate source file, also declaring the definitions with export
. You can compile the definitions in their own source file. Any other source files that need to use the template must #include
only the header file. Example 8-16 shows how to export a template.
Example 8-16: Exporting a template.
// point.h #ifndef POINT_H #define POINT_H export template<typename T> class point { public: point(T a, T b); point(); T x() const { return x_; } T y() const { return y_; } private: T x_, y_; }; #endif // POINT_H // point.cc #include "point.h" export template<typename T> point<T>::point(T a, T b) : x_(a), y_(b) {} export template<typename T> point<T>::point() : x_(T()), y_(T()) {}