C++ Extern Templates: Suppressing Instantiations
Extern templates in C++ provide a mechanism to optimize compilation
times and reduce object file sizes by explicitly controlling template
instantiation across translation units. This article explains how the
extern template syntax instructs the compiler to suppress
redundant code generation, explores the distinction between explicit
declarations and definitions, and demonstrates how to implement this
feature to improve build performance.
The Problem: Implicit Template Instantiation
In standard C++, function and class templates are usually defined
entirely within header files. When a translation unit (a
.cpp file plus its included headers) uses a template
specialization, such as std::vector<int> or a custom
MyClass<int>, the compiler implicitly instantiates
the code for that specific type inside that translation unit's object
file.
When multiple translation units include the same header and
instantiate the same template with the same template arguments, the
compiler duplicates the identical machine code across all resulting
.o or .obj files. Although the linker
eventually discards these duplicate definitions to satisfy the One
Definition Rule (ODR), generating, parsing, and optimizing redundant
template code significantly inflates compilation times and disk usage
during builds.
What Is an Extern Template?
Introduced in C++11, an extern template allows developers to tell the
compiler that a specific template instantiation is provided elsewhere.
It uses the extern keyword followed by the template
instantiation syntax:
extern template class MyClass<int>;
extern template void processData<double>(double);When the compiler encounters an extern template
declaration, it treats the symbols associated with that specialization
as external references rather than generating the definition locally. It
assumes that another translation unit will supply the actual definition,
suppressing the instantiation process in the current file.
How Extern Templates Suppress Instantiation
The mechanism relies on pairing an explicit instantiation
declaration (extern template) with an
explicit instantiation definition
(template without extern).
- Suppression in Consumers: When placed in a shared
header file, the
extern templatedirective signals to any file including that header that it should not generate code for that particular specialization. The compiler leaves the implementation undefined and emits undefined symbol references in the object file. - Definition in a Dedicated Source File: Exactly one
translation unit must contain the explicit instantiation definition
(omitting the
externkeyword). The compiler compiles the template code for that specific type here once. - Resolution at Link Time: During the link stage, the linker matches the external references generated by the suppressed translation units to the single definition emitted by the dedicated source file.
Practical Implementation
To implement extern templates, structure the template declaration, definition, and instantiation as follows:
1. The Header File
(VectorWrapper.h)
Provide the template definition, followed by the extern declarations for commonly used types:
#pragma once
template <typename T>
class VectorWrapper {
public:
void add(const T& item);
T get(int index) const;
};
// Extern template declarations: suppress instantiation across consumers
extern template class VectorWrapper<int>;
extern template class VectorWrapper<double>;2. The
Instantiation Source File (VectorWrapper.cpp)
Include the header and provide explicit instantiation definitions for
the types declared as extern:
#include "VectorWrapper.h"
// Template member implementations
template <typename T>
void VectorWrapper<T>::add(const T& item) { /* ... */ }
template <typename T>
T VectorWrapper<T>::get(int index) const { /* ... */ }
// Explicit instantiation definitions: generate code once here
template class VectorWrapper<int>;
template class VectorWrapper<double>;3. The Consumer
Source Files (main.cpp, other.cpp)
When consuming code uses these types, the compiler bypasses instantiation:
#include "VectorWrapper.h"
int main() {
VectorWrapper<int> wrapper; // Uses extern template; no local code generated
wrapper.add(42);
return 0;
}When to Use Extern Templates
Extern templates are most beneficial for heavy, complex templates that are instantiated with the same types across dozens or hundreds of translation units. By shifting compilation cost from an \(O(N)\) operation (where \(N\) is the number of translation units) to \(O(1)\), projects can achieve substantial reductions in build times and intermediate object file footprints.