52 / 163 · C++11 · 10 min
Don't mix full specialization, partial specialization, and overloading
Full specialization supplies an alternative definition for determined template arguments; partial specialization supplies an implementation for a pattern of arguments. Class templates may be partially specialized; function templates may not. Splitting function behavior usually prefers overloads or constraints, so you do not misjudge how specialization participates in selection.
In this lesson
One concrete point versus a class of patterns
The primary template gives the default rule. template<> struct Kind<int> is a full specialization and matches only one fully determined argument list. template<class T> struct Kind<T*> is a partial specialization and matches every argument that fits the pointer pattern. Partial specialization does not necessarily mean filling in only some parameters; it can also add relationships or structural constraints on all parameters.
The example splits ordinary types, pointer types, and the exact type int into three categories. Kind<double> uses the primary template, Kind<double*> matches the pointer partial specialization, and Kind<int> uses the explicit full specialization. Output comes from the static function in the chosen definition, not from a runtime type check that then branches.
A specialization is an independent definition and does not inherit members automatically
A class-template specialization is a replacement definition. It does not automatically acquire a member just because the primary template has that member. If a common interface must stay consistent, each definition should provide it; truly shared implementation can also be extracted, but do not twist the type-selection structure into something unreadable merely to save a few lines.
When several partial specializations match at once, the compiler uses partial ordering to find the more specialized one. If a unique best candidate cannot be compared, the program is ambiguous. Do not read this as later source overwriting earlier source. The primary template should be declared first, and related specializations must also be visible at the point of instantiation that uses them; otherwise cross-file behavior may go wrong.
Functions usually prefer overloading
Function templates support explicit full specialization but not partial specialization. To treat T* specially, usually add a function-template overload whose parameter is T*. C++20 can also use concepts to express clearer candidate conditions. A full specialization does not participate in the initial overload-resolution step as an independent candidate, so mixing overloads and specializations often defies intuition.
This example uses a type_traits static_assert to establish a C++11 baseline for the declared version, while template specialization itself predates C++11. Real projects prefer the most local tool: consider class specialization when one type changes storage layout; consider overloads, constraints, or if constexpr when a family of types takes different algorithms.
Pitfalls
- Function templates cannot be partially specialized; something that looks like
f<T*>(...)is not a valid function-template overload declaration. - Do not casually add specializations to
std; the standard library accepts user specializations only in clearly allowed cases that meet the requirements, and many traits explicitly forbid this.
Run an example
Minimum C++11 · complete program · Download .cpp
#include <cassert>
#include <iostream>
#include <type_traits>
template<class T>
struct Kind { static int code() { return 0; } };
template<class T>
struct Kind<T*> { static int code() { return 1; } };
template<>
struct Kind<int> { static int code() { return 2; } };
int main() {
static_assert(std::is_pointer<double*>::value, "pointer case");
assert(Kind<double>::code() == 0);
assert(Kind<double*>::code() == 1);
assert(Kind<int>::code() == 2);
assert(Kind<const int*>::code() == 1);
std::cout << Kind<double>::code() << ' ' << Kind<double*>::code()
<< ' ' << Kind<int>::code() << '\n';
}
Compile locally
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread modern-specialization.cpp -o example && ./exampleExpected result
0 1 2
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
Does Kind<int*> select the full specialization of Kind<int>? If you only want int* to return 3, what should you add?
Show a reference answer
No. int* and int are different types; the existing match is the pointer partial specialization, returning 1. Add template<> struct Kind<int*> { static int code() { return 3; } }; and place it before the first related instantiation to give int* an exact replacement definition; other pointers still take the partial specialization.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.