Specialization supplies a definition for a particular template argument list or, for class and variable templates, for a more specific argument pattern. Select specialization only when type-based dispatch is the appropriate interface; use ordinary overloads for function-specific dispatch and concepts for behavioral eligibility.
Full Specializations #
A full specialization names every argument and begins with template <>. Function, class, and variable templates can be fully specialized.
template <class T>
struct formatter {
static const char* name() { return "other"; }
};
template <>
struct formatter<int> {
static const char* name() { return "int"; }
};A full specialization is an independent entity. It does not inherit members, definitions, or default template arguments from the primary template; define everything it needs. Declare a full specialization before the first use that would otherwise implicitly instantiate that specialization in every translation unit that uses it.
Partial Specializations #
Class and variable templates can have partial specializations. A partial specialization matches a pattern, and the compiler chooses the most specialized matching pattern through partial ordering.
#include <type_traits>
template <class T>
struct is_pointer_like : std::false_type {};
template <class T>
struct is_pointer_like<T*> : std::true_type {};
template <class T>
inline constexpr bool is_pointer_like_v = is_pointer_like<T>::value;A partial specialization needs a primary template declaration. Its argument list must be more specialized than the primary and must remain deducible under the language rules. If matching partial specializations are incomparable, use is ambiguous and compilation fails.
Function templates cannot be partially specialized. Add overloaded function templates, constrain them, or dispatch through a specialized class/variable trait instead.
Ordering and Library Boundaries #
Place the primary and declarations of applicable specializations in a header when callers might implicitly instantiate them. Define a specialization in one translation unit only when the declaration is visible before every potential implicit-instantiation point and linkage is arranged accordingly.
Specializing a trait can create a clean dispatch boundary:
struct LocalPacket {};
template <class T>
inline constexpr bool uses_fast_path_v = false;
template <>
inline constexpr bool uses_fast_path_v<LocalPacket> = true;The program owns LocalPacket and the trait, so the contract is explicit. Do not use specialization to bypass access control or alter unrelated semantics.
The standard library is different. Unless a library component says otherwise, a program may add a specialization of a standard-library template to namespace std only when the declaration depends on at least one program-defined type and the specialization meets the original template’s requirements. Specific components can prohibit specialization or impose additional requirements. Partial specialization remains available only for class and variable templates under the ordinary language rules; function templates cannot be partially specialized. Do not infer permission to add an overload, primary template, or unrelated declaration to std. Prefer documented customization points such as std::hash and std::formatter specializations, argument-dependent swap, ranges customization, or a project-owned trait.
Comments
Read or comment on GitHub (sign-in required to comment)