5.2.15 · D5C++ Programming

Question bank — Template specialization — full and partial

1,684 words8 min readBack to topic

True or false — justify

A full specialization must begin with template<>.
True — a full specialization leaves zero free parameters, so the angle brackets after template are empty; omitting them makes the compiler read it as a redeclaration of the primary.
A partial specialization can leave template<...> (that is, a non‑empty comma‑separated parameter list) present.
True — partial means some parameters stay free, so those free names live inside that list (e.g. template<class T> struct Box<T*>).
Function templates can be partially specialized just like class templates.
False — function templates support full specialization only; for partial-like behaviour you use overloading instead (picking the best match among functions), because overload resolution already handles that job.
For int*, the partial T* is more specialized than the primary T.
True — every type matching T* (all pointers) also matches T, but not vice-versa, so T* describes a strict subset and therefore wins by partial ordering.
If two partial specializations both match a type, the compiler picks the one written first in the file.
False — declaration order is irrelevant; it picks the most specialized one, and if neither is more specialized than the other it reports an ambiguity error.
A specialization may be placed in any namespace as long as the name matches.
False — a specialization must live in the same namespace as the primary template, otherwise it is a different, unrelated template.
std::vector<bool> is just std::vector<T> with T = bool.
False — it is a special container specialization that bit-packs the booleans, so it does not store real bool objects and even behaves differently on element access. This bit-packing is the whole reason std::vector<bool> special case exists as a documented gotcha.
A partial specialization can introduce a new default template argument.
False — default template arguments belong only on the primary template; a partial spec may use them but not declare new ones.
You may specialize a template after the compiler has already instantiated it for that type.
False — the specialization must be visible before first use; specializing afterwards is ill-formed (often no diagnostic required, which makes it dangerous).
constexpr if can replace some uses of specialization.
True — inside one template body, constexpr if can select different code paths per compile-time condition without writing separate specialization structs.

Spot the error

template<class T> struct Box { };
struct Box<int> { };   // full spec for int?
What is wrong?
The full specialization is missing template<>; without it this line looks like a redefinition of a non-template struct Box<int> and won't compile as a specialization.
template<class T> T maxi(T a, T b);
template<class T> T maxi<T*>(T* a, T* b);  // "partial spec" for pointers
What is wrong?
Function templates cannot be partially specialized, so maxi<T*> is illegal; write a second maxi(T* a, T* b) overload instead.
namespace lib { template<class T> struct H { }; }
template<> struct lib::H<int> { };  // at global scope
Is this correct?
This form is actually allowed for full specializations if written with the qualified name — but a common error is opening a different namespace or forgetting the lib:: qualifier, which produces a distinct template rather than a specialization of lib::H.
template<class A,class B> struct M { };
template<class A> struct M<A,int> { };
template<class B> struct M<int,B> { };
M<int,int> m;
Why won't this compile?
Both partials match M<int,int> and neither is more specialized than the other, so the compiler cannot choose and reports an ambiguity error.
template<class T = int> struct P { };
template<class T = char> struct P<T*> { };  // new default in partial
What rule is broken?
A partial specialization may not introduce a new default template argument (= char); defaults are only permitted on the primary.
template<class T> struct Trait { static const bool ptr = false; };
Trait<int*> a;
template<class T> struct Trait<T*> { static const bool ptr = true; };
What is subtly wrong?
The partial Trait<T*> is declared after Trait<int*> was already instantiated; the specialization must appear before that first use or the program is ill-formed.

Why questions

Why do classes get partial specialization but functions do not?
Classes have no overloading mechanism, so they need partial specialization to vary behaviour by type shape; functions already resolve the "best match" through overload resolution, so partial was left out to avoid two competing rules.
Why must a full specialization write the type in <...> after the name but empty <> before it?
The empty template<> announces "no parameters remain to be filled," while Box<int> names which instantiation of the primary you are replacing — the two brackets answer different questions.
Why is T* considered "more specialized" than T?
Because the set of types matching T* (pointers) is a strict subset of those matching T (everything); a narrower pattern signals a clearer intent, so partial ordering ranks it above the unconstrained primary.
Why does specialization keep the same name and interface instead of a new function?
So the caller's code never changes — the special case swaps only the implementation, letting the compiler transparently route the right type to the right recipe, which is how type traits hide their machinery.
Why is std::is_pointer typically built with a partial specialization?
The primary sets value = false, and the partial is_pointer<T*> sets value = true, so matching the pointer shape is exactly what distinguishes pointer types — this is the canonical use of partial specialization.
Why does specialization matter more for performance than for correctness sometimes?
A generic recipe can be correct but slow or wasteful (like storing full bytes for booleans); a specialization such as std::vector<bool> swaps in a tighter representation for the same interface.

Edge cases

First, the mini-template these edge cases refer to:

template<class A, class B> struct Pair2 { static const char* k(){ return "AB"; } };
template<class B>          struct Pair2<int, B> { static const char* k(){ return "intB"; } };
If a type matches only the primary template and no specialization, what happens?
It uses the primary template — the primary is always the fallback for anything no specialization matches.
If both a full and a partial specialization match a type, which wins?
The full specialization always wins, because fixing all parameters to concrete types is the most specific possible match and beats any partial.
Using the Pair2 above, what happens when you instantiate Pair2<int, char>?
The partial Pair2<int, B> fires with B = char because the first parameter is pinned to int while B stays free — only some parameters are fixed, which is the definition of partial.
Can you specialize a template for const int separately from int?
Yes — const int and int are distinct types, so Trait<const int> and Trait<int> are independent specializations that will not collide.
What does the primary template do if it is only declared but never defined, and every real type is specialized?
It can compile fine as long as no unspecialized type is ever instantiated — the undefined primary acts as a "compile error trap" for any type you forgot to handle.
Does a partial specialization of a class template for T& (reference) work the same as for T*?
Yes — you can match the reference shape just like the pointer shape, and partial ordering treats T& as a strict subset of T in exactly the same way.
If two overloads and a specialization could all apply to a function call, what resolves first?
Overload resolution picks among the function templates and non-templates first; a full specialization does not participate as an independent candidate — it only defines the body chosen once the primary template is selected. For finer control between candidates, see Tag Dispatch and Policy-based Design.

Recall One-line self-test

Cover every answer above; if your justification names the reason (subset, same-namespace, before-use, no-partial-functions) and not just "true/false," you own the concept.