5.2.15 · D4C++ Programming

Exercises — Template specialization — full and partial

2,053 words9 min readBack to topic

Vocabulary reminder (all built in the parent note):

  • Primary template — the generic fallback recipe.
  • Full specializationtemplate<> with the exact type pinned, e.g. Box<int>.
  • Partial specialization — a shape pinned (like T* or pair<A,B>), some params still free.
  • The compiler picks the most specialized matching pattern; ties with no winner are an ambiguity error.

Level 1 — Recognition

Recall Solution 1.1
  • (a) primary — one free parameter T, no pattern on the name.
  • (b) fulltemplate<> (zero free params), exact type int pinned.
  • (c) partialT is still free but the shape T* is pinned.
  • (d) primary — two free params, no pattern.
  • (e) partial — first param pinned to int, B still free.
Recall Solution 1.2
  • (i) is legal — function templates can be fully specialized.
  • (ii) is illegal — function templates cannot be partially specialized. You would use an overload template<class T> void f(T*) instead (see Overload Resolution vs Specialization).

Level 2 — Application

Recall Solution 2.1
  • (a) double matches no pattern → primary0.
  • (b) char matches the full spec S<char>1.
  • (c) char* — does it match S<char>? No, char* is not char. It matches S<T*> with T=char2.
  • (d) int** is a pointer (to int*), so S<T*> fires with T=int*2.

Answers: 0, 1, 2, 2.

Recall Solution 2.2
template<class T> struct Unwrap<T*> { using type = T; };

For Unwrap<int**>: the pattern T* matches with T = int*, so ::type is int* — it strips one level of pointer, not all. To strip fully you would recurse, but that is beyond this single spec.


Level 3 — Analysis

Recall Solution 3.1
  • (a) <double,char> matches only the primary → 0.
  • (b) <double,int> matches M<A,int> only → 1.
  • (c) <int,double> matches M<int,B> only → 2.
  • (d) <int,int> matches M<A,int>, M<int,B>, and M<int,int>. But the full specialization M<int,int> is the most specific of all and wins outright → 3.

Answers: 0, 1, 2, 3. Note: without line for M<int,int> (the full spec), case (d) would be an ambiguity error because M<A,int> and M<int,B> tie.

Recall Solution 3.2

Both P1 (<A,int>) and P2 (<T,T>) match N<int,int>. To break the tie, ask: is one pattern strictly more specialized (matches a subset of the other)?

  • P1 matches every <X,int>; P2 matches every <X,X>. Neither set contains the other (e.g. <double,int> matches P1 not P2; <char,char> matches P2 not P1).
  • So neither is more specializedambiguity error.

Level 4 — Synthesis

Recall Solution 4.1
template<class T> struct RemoveConst          { using type = T; };
template<class T> struct RemoveConst<const T> { using type = T; };
  • RemoveConst<const int> matches the partial const T with T=int::type = int.
  • RemoveConst<int> matches only the primary → ::type = int.

This is exactly how std::remove_const is built — see Type Traits and std::is_pointer.

Recall Solution 4.2
template<class T>          struct Rank            { static const int value = 0; };
template<class T>          struct Rank<T[]>       { static const int value = 1 + Rank<T>::value; };
template<class T, unsigned N> struct Rank<T[N]>   { static const int value = 1 + Rank<T>::value; };

Trace for int[][3] (an array of int[3], unknown outer size):

  1. Matches Rank<T[]> with T = int[3]value = 1 + Rank<int[3]>::value.
  2. Rank<int[3]> matches Rank<T[N]> with T=int, N=3value = 1 + Rank<int>::value.
  3. Rank<int> matches primary → 0.
  4. Unwind: 1 + (1 + 0) = 2. ✓

Level 5 — Mastery

Recall Solution 5.1

Ill-formed program. A specialization must appear before the type's first instantiation. Here Traits<int> is instantiated at Traits<int> a;, then specialized — the compiler already committed to the primary (id = 0). The correct value the student intended was 9, but the ordering violation makes the program ill-formed (a diagnostic is required). Fix: move the template<> specialization above Traits<int> a;.

Recall Solution 5.2
  • Way A is legal (full spec of a function template is allowed). pick<int>()1, pick<double>()0.
  • Way B relies on overload resolution: pick2(int) (a non-template exact match) beats the template for int. pick2(5)1, pick2(5.0)0.
  • Best practice for functions is Way B (overloading), because partial-like behaviour on functions must use overloads, and overloads compose more predictably. See Overload Resolution vs Specialization and Tag Dispatch and Policy-based Design.
Recall Solution 5.3
  • Fmt<double> → no pattern but primary → 0.
  • Fmt<double*> → matches Fmt<T*> (T=double); no full spec for double*1.
  • Fmt<int*> → matches primary? no. Fmt<T*>? yes. Fmt<int*> full spec? yes — full wins2.

Answers: 0, 1, 2.


Recall Master check — do it from memory

Selection order, one line ::: full spec first → most-specialized partial → primary → else ambiguity if partials tie. Fmt<int*> with primary, T*, and full int* ::: full int* wins. Rank<int[][3]>::value ::: 2. Why Way B for functions ::: functions can't be partially specialized; overloads give the same effect predictably.