cpp core · advanced

Templates: Full Specialization, Partial Specialization, Explicit Instantiation

Template specialization: primary template = fallback; specializations = overrides. Full specialization (template<>): fixes ALL template parameters; works for both classes and functions. Partial specialization: fixes SOME parameters; class templates ONLY (not function templates). Compiler selects most specific match: full > partial > primary. Function template specialization pitfall: specs not in overload resolution; overloads preferred — prefer regular function overloads over function template specializations (Herb Sutter rule). Type traits via specialization: primary=false_type, T* specialization=true_type — basis of <type_traits>. Explicit instantiation: template class Foo<double>; in one .cpp = generate once. extern template class Foo<double>; in header = suppress elsewhere. When to use: specialization for different struct layout; if constexpr for different body; concepts for overload selection.

🔑 Key line

Full specialization: template<> struct Foo<int> {} — fixes all params; partial specialization: template<typename T> struct Foo<T*> {} — fixes some (class templates only); prefer overloading over function template specialization; explicit instantiation for build-time reduction.

The code

// Template Specialization — full and partial
// Primary template:
template <typename T>
struct Storage {
void store(T val) { /* generic */ }
T load() { /* generic */ }
};
// FULL specialization — fix ALL template params:
template <>
struct Storage<bool> { // specific to bool only
void store(bool val) {
data_ = val ? 1 : 0; /* pack into byte */
}
bool load() {
return data_ != 0;
}
private:
uint8_t data_{};
};
// PARTIAL specialization — fix SOME params (only for class templates):
template <typename T>
struct Storage<T*> { // specialization for any pointer type
void store(T* p) {
ptr_ = p;
}
T* load() {
return ptr_;
}
private:
T* ptr_{};
};
// Usage — compiler picks most specific match:
Storage<int> s1; // primary template
Storage<bool> s2; // full specialization
Storage<int*> s3; // partial specialization (T=int, T*=int*)
// FUNCTION template specialization:
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
template <>
const char* max<const char*>(const char* a, const char* b) {
return strcmp(a, b) > 0 ? a : b; // string compare, not pointer compare
}
// Prefer overloading over function specialization!
// Function specializations don't participate in overload resolution
// → unintuitive: overload of primary is preferred over specialization
// Explicit instantiation — force generation without calling:
template class Storage<double>; // generates Storage<double> in this TU
// Prevents implicit instantiation in other TUs → faster build
// extern template — suppress implicit instantiation in THIS TU:
extern template class Storage<int>; // definition elsewhere

What this lesson walks through

  1. 01Primary template and full specialization
  2. 02Partial specialization — only for class templates
  3. 03Function template specialization — prefer overloading
  4. 04Explicit instantiation — controlling template code generation
  5. 05Specialization for type traits — implementing is_pointer
  6. 06Template specialization vs if constexpr — when to use each

A primary template provides the generic behavior. A full specialization (template<>) overrides it for ONE specific set of template arguments. The compiler uses the most specific match. Full specialization: all template parameters are fixed. Useful when a type requires completely different behavior — e.g. Storage<bool> packs bits instead of storing a full bool.

See it animated — step by step, at your own pace

Unlock the full interactive walkthrough of Templates: Full Specialization, Partial Specialization, Explicit Instantiation and 100+ animated C++ interview lessons.

← Previous
Templates: Variadic Templates, Fold Expressions, Parameter Pack Patterns
Next →
Move Semantics: Copy vs Move