Chapter 10

Basic Template Terminology

So far we have introduced the basic concept of templates in C++. Before we go into details, let’s look at the terminology we use. This is necessary because, inside the C++ community (and even in an early version of the standard), there is sometimes a lack of precision regarding terminology.

10.1 “Class Template” or “Template Class”?

In C++, structs, classes, and unions are collectively called class types. Without additional qualification, the word “class” in plain text type is meant to include class types introduced with either the keyword class or the keyword struct.1 Note specifically that “class type” includes unions, but “class” does not.

There is some confusion about how a class that is a template is called:

• The term class template states that the class is a template. That is, it is a parameterized description of a family of classes.

• The term template class, on the other hand, has been used

– as a synonym for class template.

– to refer to classes generated from templates.

– to refer to classes with a name that is a template-id (the combination of a template name followed by the template arguments specified between < and >).

The difference between the second and third meanings is somewhat subtle and unimportant for the remainder of the text.

Because of this imprecision, we avoid the term template class in this book.

Similarly, we use function template, member template, member function template, and variable template but avoid template function, template member, template member function, and template variable.

10.2 Substitution, Instantiation, and Specialization

When processing source code that uses templates, a C++ compiler must at various times substitute concrete template arguments for the template parameters in the template. Sometimes, this substitution is just tentative: The compiler may need to check if the substitution could be valid (see Section 8.4 on page 129 and Section 15.7 on page 284).

The process of actually creating a definition for a regular class, type alias, function, member function, or variable from a template by substituting concrete arguments for the template parameters is called template instantiation.

Surprisingly, there is currently no standard or generally agreed upon term to denote the process of creating a declaration that is not a definition through template parameter substitution. We have seen the phrases partial instantiation or instantiation of a declaration used by some teams, but those are by no means universal. Perhaps a more intuitive term is incomplete instantiation (which, in the case of a class template, produces an incomplete class).

The entity resulting from an instantiation or an incomplete instantiation (i.e., a class, function, member function, or variable) is generically called a specialization.

However, in C++ the instantiation process is not the only way to produce a specialization. Alternative mechanisms allow the programmer to specify explicitly a declaration that is tied to a special substitution of template parameters. As we showed in Section 2.5 on page 31, such a specialization is introduced with the prefix template<>:

template<typename T1, typename T2>        // primary class template
class MyClass {
   …
};

template<>                                // explicit specialization
class MyClass<std::string,float> {
   …
};

Strictly speaking, this is called an explicit specialization (as opposed to an instantiated or generated specialization).

As described in Section 2.6 on page 33, specializations that still have template parameters are called partial specializations:

template<typename T>        // partial specialization
class MyClass<T,T> {
    …
};

template<typename T>        // partial specialization
class MyClass<bool,T> {
    …
};

When talking about (explicit or partial) specializations, the general template is also called the primary template.

10.3 Declarations versus Definitions

So far, the words declaration and definition have been used only a few times in this book. However, these words carry with them a rather precise meaning in standard C++, and that is the meaning that we use.

A declaration is a C++ construct that introduces or reintroduces a name into a C++ scope. This introduction always includes a partial classification of that name, but the details are not required to make a valid declaration. For example:

class C;           // a declaration of C as a class
void f(int p);     // a declaration of f() as a function and p as a named parameter
extern int v;      // a declaration of v as a variable

Note that even though they have a “name,” macro definitions and goto labels are not considered declarations in C++.

Declarations become definitions when the details of their structure are made known or, in the case of variables, when storage space must be allocated. For class type definitions, this means a brace-enclosed body must be provided. For function definitions, this means a brace-enclosed body must be provided (in the common case), or the function must be designated as = default2 or = delete. For a variable, initialization or the absence of an extern specifier causes a declaration to become a definition. Here are examples that complement the preceding nondefinition declarations:

class C {};               // definition (and declaration) of class C

void f(int p)    {        //definition (and declaration) of function f()
  std::cout << p << ’\n’;
}
extern int v = 1;         // an initializer makes this a definition for v
int w;                    // global variable declarations not preceded by
                          // extern are also definitions

By extension, the declaration of a class template or function template is called a definition if it has a body. Hence,

    template<typename T>
    void func (T);

is a declaration that is not a definition, whereas

template<typename T>
class S {};

is in fact a definition.

10.3.1 Complete versus Incomplete Types

Types can be complete or incomplete, which is a notion closely related to the distinction between a declaration and a definition. Some language constructs require complete types, whereas others are valid with incomplete types too.

Incomplete types are one of the following:

• A class type that has been declared but not yet defined.

• An array type with an unspecified bound.

• An array type with an incomplete element type.

void

• An enumeration type as long as the underlying type or the enumeration values are not defined.

• Any type above to which const and/or volatile are applied. All other types are complete. For example:

class C;              // C is an incomplete type
C const* cp;          // cp is a pointer to an incomplete type
extern C elems[10];   // elems has an incomplete type
extern int arr[];     // arr has an incomplete type

class C { };          // C now is a complete type (and therefore cpand elems
                      // no longer refer to an incomplete type)
int arr[10];          // arr now has a complete type

See Section 11.5 on page 171 for hints about how to deal with incomplete types in templates.

10.4 The One-Definition Rule

The C++ language definition places some constraints on the redeclaration of various entities. The totality of these constraints is known as the one-definition rule or ODR. The details of this rule are a little complex and span a large variety of situations. Later chapters illustrate the various resulting facets in each applicable context, and you can find a complete description of the ODR in Appendix A. For now, it suffices to remember the following ODR basics:

• Ordinary (i.e., not templates) noninline functions and member functions, as well as (noninline) global variables and static data members should be defined only once across the whole program.3

• Class types (including structs and unions), templates (including partial specializations but not full specializations), and inline functions and variables should be defined at most once per translation unit, and all these definitions should be identical.

A translation unit is what results from preprocessing a source file; that is, it includes the contents named by #include directives and produced by macro expansions.

In the remainder of this book, linkable entity refers to any of the following: a function or member function, a global variable or a static data member, including any such things generated from a template, as visible to the linker.

10.5 Template Arguments versus Template Parameters

Compare the following class template:

template<typename T, int N>
class ArrayInClass {
  public:
    T array[N];
};

with a similar plain class:

class DoubleArrayInClass {
  public:
    double array[10];
};

The latter becomes essentially equivalent to the former if we replace the parameters T and N by double and 10 respectively. In C++, the name of this replacement is denoted as

ArrayInClass<double,10>

Note how the name of the template is followed by template arguments in angle brackets.

Regardless of whether these arguments are themselves dependent on template parameters, the combination of the template name, followed by the arguments in angle brackets, is called a template-id.

This name can be used much like a corresponding nontemplate entity would be used. For example:

int main()
{
   ArrayInClass<double,10>
   ad; ad.array[0] = 1.0;
}

It is essential to distinguish between template parameters and template arguments. In short, you can say that “parameters are initialized by arguments.”4 Or more precisely:

Template parameters are those names that are listed after the keyword template in the template declaration or definition (T and N in our example).

Template arguments are the items that are substituted for template parameters (double and 10 in our example). Unlike template parameters, template arguments can be more than just “names.”

The substitution of template parameters by template arguments is explicit when indicated with a template-id, but there are various situations when the substitution is implicit (e.g., if template parameters are substituted by their default arguments).

A fundamental principle is that any template argument must be a quantity or value that can be determined at compile time. As becomes clear later, this requirement translates into dramatic benefits for the run-time costs of template entities. Because template parameters are eventually substituted by compile-time values, they can themselves be used to form compile-time expressions. This was exploited in the ArrayInClass template to size the member array array. The size of an array must be a constant-expression, and the template parameter N qualifies as such.

We can push this reasoning a little further: Because template parameters are compile-time entities, they can also be used to create valid template arguments. Here is an example:

template<typename T>
class Dozen {
  public:
   ArrayInClass<T,12> contents;
};

Note how in this example the name T is both a template parameter and a template argument. Thus, a mechanism is available to enable the construction of more complex templates from simpler ones. Of course, this is not fundamentally different from the mechanisms that allow us to assemble types and functions.

10.6 Summary

• Use class template, function template, and variable template for classes, functions, and variables, respectively, that are templates.

Template instantiation is the process of creating regular classes or functions by replacing template parameters with concrete arguments. The resulting entity is a specialization.

• Types can be complete or incomplete.

• According to the one-definition rule (ODR), noninline functions, member functions, global variables, and static data members should be defined only once across the whole program.

1 In C++, the only difference between class and struct is that the default access for class is private, whereas the default access for struct is public. However, we prefer to use class for types that use new C++ features, and we use struct for ordinary C data structure that can be used as “plain old data” (POD).

2 Defaulted functions are special member functions that will be given a default implementation by the compiler, such as the default copy constructor.

3 Global and static variables and data members can be defined as inline since C++17. This removes the requirement that they be defined in exactly one translation unit.

4 In the academic world, arguments are sometimes called actual parameters, whereas parameters are called formal parameters.