Explicit Instantiation



Explicit Instantiation

It is possible to create explicitly a point of instantiation for a template specialization. The construct that achieves this is called an explicit instantiation directive. Syntactically, it consists of the keyword template followed by a declaration of the specialization to be instantiated. For example:

template<typename T> 
void f(T) throw(T) 
{ 
} 

// four valid explicit instantiations: 
template void f<int>(int) throw(int); 
template void f<>(float) throw(float); 
template void f(long) throw(long); 
template void f(char); 

Note that every instantiation directive is valid. Template arguments can be deduced (see Chapter 11), and exception specifications can be omitted. If they are not omitted, they must match the one of the template.

Members of class templates can also be explicitly instantiated in this way:

template<typename T> 
class S { 
  public: 
    void f() { 
    } 
}; 

template void S<int>::f(); 

template class S<void>; 

Furthermore, all the members of a class template specialization can be explicitly instantiated by explicitly instantiating the class template specialization.

Many early C++ compilation systems did not have automatic instantiation capabilities when they first implemented support for templates. Instead, some systems required that the function template specializations used by a program be manually instantiated in a single location. This manual instantiation usually involved implementation-specific #pragma directives.

The C++ standard therefore codified this practice by specifying a clean syntax for it. The standard also specifies that there can be at most one explicit instantiation of a certain template specialization in a program. Furthermore, if a template specialization is explicitly instantiated, it should not be explicitly specialized, and vice versa.

In the original context of manual instantiations, these limitations may seem harmless, but in current practice they cause some grief.

First, consider a library implementer who releases a first version of a function template:

// File toast.hpp: 
template<typename T> 
void toast(T const& x) 
{ 
     
} 

Client code is free to include this header and explicitly instantiate its template:

// Client code: 
#include "toast.hpp" 

template void toast(float); 

Unfortunately, if the library writer decides to specialize toast<float> explicitly, the client code becomes invalid. This is even more delicate when the library is a standard library implemented by different vendors. Some may explicitly specialize some standard templates, whereas others may not (or may specialize different specializations). The client code can therefore not specify the explicit instantiation of library components in a portable manner.

At the time of this writing (2002), the C++ standardization committee appears inclined to state that if an explicit instantiation directive follows an explicit specialization for the same entity, then the directive is without effect. (The final decision in this matter is still pending and may not occur if it appears technically infeasible.)

A second challenge with the current limitations on explicit template instantiation stems from their use as a means to improve compilation times. Indeed, many C++ programmers have observed that automatic template instantiation has a nontrivial negative impact on build times. A technique to improve build times consists in manually instantiating certain template specializations in a single location and inhibiting the instantiation in all other translation units. The only portable way to ensure this inhibition is not to provide the template definition except in the translation unit where it is explicitly instantiated. For example:

// Translation unit 1: 
template<typename T> void f(); // no definition: prevents instantiation 
                               // in this translation unit 
void g() 
{ 
    f<int>(); 
} 

// Translation unit 2: 
template<typename T> void f() 
{ 
} 

template void f<int>();        // manual instantiation 

void g(); 

int main() 
{ 
    g(); 
} 

This solution works well, but it requires control of the source code that provides the template interface. Often, this is not the case. The source code providing the template cannot be modified and always provides the definition of the templates.

One "trick" that is sometimes used is to declare a template as specialized in all translation units (which does inhibit the automatic instantiation of that specialization) except in the translation unit in which that specialization is explicitly instantiated. To illustrate this, let's modify our previous example to include a definition for the template:

// Translation unit 1: 
template<typename T> void f() 
{ 
} 

template<> void f<int>();  // declared but not defined 

void g() { 
    f<int>(); 
} 

// Translation unit 2: 
template<typename T> void f() 
{ 
} 

template void f<int>();    // manual instantiation 

void g(); 

int main() 
{ 
    g(); 
} 

Unfortunately, this assumes that the object code for a call to an explicitly specialized specialization is identical to a call to the matching generic specialization. This assumption is not correct. Several C++ compilers generate different mangled names for the two entities. [15] With these compilers, the code does not link to a complete executable program.

[15] The mangled name of a function is the name seen by the linker. It combines the plain function name with attributes of its parameters, its template arguments, and sometimes some other properties to generate a unique name that does not clash with validly overloaded functions.

Some compilers provide an extension to indicate that a template specialization should not be instantiated in that translation unit. A popular (but nonstandard) syntax consists in prepending the keyword extern before an explicit instantiation directive that would otherwise trigger the instantiation. The first file in our last example can be rewritten as follows for compilers supporting that extension:

// Translation unit 1: 
template<typename T> void f() 
{ 
} 

extern template void f<int>();  // declared but not defined 

void g() 
{ 
    f<int>(); 
}