C++ Template Specialisation and Deduction: Predict Which Case Wins
Predict which C++ template definition wins by separating class specialisation, function overload resolution, ordinary deduction, and C++17 CTAD.
KnowledgeGate Team
Exam prep & CS education

A generic template looks simple until one type needs different behaviour, a pointer should choose another path, or the compiler deduces a type you did not expect. Class and function template specialisation follow different rules, while C++17 class template argument deduction selects class template arguments from constructor syntax. To predict the selected definition, list candidates, deduce template arguments, and rank the viable cases before compiling.
1. C++ template specialisation and deduction solve different jobs
Mechanism | Job |
|---|---|
Primary template | Provides the fallback definition |
Full class specialisation | Handles one exact template argument list |
Partial class specialisation | Handles an argument pattern such as |
Function template overload | Adds a competing callable pattern |
Template argument deduction | Finds missing template arguments from syntax |
Ordering or overload resolution | Chooses which viable definition wins |
Specialisation changes implementation for selected arguments; deduction discovers arguments. Neither is a runtime if: 7 versus 9 does not change the Serializer<int> definition.
Place templates in the wider Coding & DSA sequence. Templates in C++: Function and Class Templates with Runnable Examples establishes function, class and non-type template fundamentals; specialisation, overload resolution and deduction-guide ordering answer the narrower question of which definition wins.
2. Full and partial class template specialisation with Serializer
This class-template family has three definitions:
template<class T>
struct Serializer {
static std::string write(const T&) { return "generic"; }
};
template<>
struct Serializer<bool> {
static std::string write(bool value) {
return value ? "true" : "false";
}
};
template<class T>
struct Serializer<T*> {
static std::string write(T* value) {
return value == nullptr ? "null" : "pointer";
}
};With int n=7; int* p=&n;, trace the template-id first:
Serializer<int>::write(7)matches no specialisation. The primary returns"generic".Serializer<bool>::write(false)exactly matches the full specialisation. It returns"false".Serializer<int*>::write(p)matchesT*withT=int. The partial specialisation returns"pointer".Serializer<int*>::write(nullptr)names that sameSerializer<int*>class first.nullptrthen converts to the already knownint*parameter type, and the function returns"null".
The primary preserves the default, the full specialisation isolates one type, and the partial controls every pointer-shaped type.
3. Class template matching: exact type details decide the winner
Template-id | Selected definition | Reason |
|---|---|---|
| primary |
|
| full specialisation | The exact argument list is |
| partial specialisation | The pointer pattern matches with |
| partial specialisation | The pointer pattern matches with |
| partial specialisation | The argument is a pointer, not exact |
| primary |
|
| primary | An array template argument is not a pointer template argument |
Use an exact full specialisation when it exists. Otherwise choose the uniquely most specialised matching partial, or the primary. Equally specialised matching partials are ambiguous.
Call-site conversions do not rewrite the class template-id. Serializer<const bool> does not become Serializer<bool>, and Serializer<int[3]> does not decay to Serializer<int*> merely because arrays often decay in function calls.

4. Function templates use overloads, not partial specialisation
Function templates may be overloaded or fully specialised, but cannot be partially specialised. For a parameter shape such as T*, write another overload:
template<class T>
std::string route(T) { return "generic"; }
template<class T>
std::string route(T*) { return "pointer-template"; }
std::string route(const char*) { return "text-overload"; }Given int n=42; int* p=&n; const char* text="kg";, trace every candidate:
route(42): onlyroute(T)is viable. It deducesT=intand returns"generic".route(p): both templates are exact matches; theconst char*overload is not viable.route(T*)is the more specialised template, deducesT=int, and returns"pointer-template".route(text): both templates and the non-template are exact matches.route(T*)is more specialised thanroute(T), then the non-template wins the remaining tie and returns"text-overload".
An explicit specialisation has a subtle relationship with overload resolution:
template<class T>
std::string pick(T) { return "primary"; }
template<>
std::string pick<int*>(int*) { return "specialisation"; }
template<class T>
std::string pick(T*) { return "pointer-overload"; }For int*, overload resolution selects pick(T*). The explicit specialisation is not a separate candidate. Use overloads for call shapes; specialise only with an intentional primary relationship.
5. Template deduction and C++17 CTAD: compute the type first
Function deduction and class template argument deduction (CTAD) have different contexts. In route(p), T* against int* yields T=int. In route(nullptr), T* cannot be deduced from std::nullptr_t; the by-value primary deduces that type and returns "generic". Deduction does not first convert nullptr.
Now add a user-defined deduction guide:
template<class T>
struct Box {
T value;
explicit Box(T input) : value(std::move(input)) {}
};
Box(const char*) -> Box<std::string>;
Box a{42};
Box b{"kg"};The implicit constructor guide makes a a Box<int> containing 42. The user-defined guide makes b a Box<std::string> containing "kg", not a const char*. Confirm both deductions:
static_assert(std::is_same_v<decltype(a), Box<int>>);
static_assert(std::is_same_v<decltype(b), Box<std::string>>);CTAD selects Box<std::string> before construction. It neither creates a specialisation nor inspects the runtime contents of "kg".

6. C++ template traps: diagnose the type before changing the code
Symptom | Why it happens | Correct move |
|---|---|---|
Attempting function-template partial specialisation | The language does not allow it | Write a function-template overload |
Expecting |
| Specialise deliberately or use the primary |
Expecting an array template argument to match | The template-id contains an array type | Handle an array pattern explicitly if needed |
Assuming | Deduction does not convert | Supply a pointer type or accept the primary |
Defining an explicit specialisation after an earlier use | The primary may already have been instantiated | Declare the specialisation before the first relevant use |
Creating overlapping partial specialisations | No match may be uniquely best | Redesign for one winner |
Debug in order: write the exact type; list viable templates and overloads; deduce without invented conversions; rank survivors; confirm with static_assert, a diagnostic, or a minimal test.
Runtime virtual dispatch chooses an override through a polymorphic object. C++ Function and Class Templates: Build One Type-Safe Range Pattern builds a reusable range pattern from generic functions and classes. Once candidate templates exist, exact types, partial ordering and overload resolution determine the winner.
7. How interviews and exam-style questions test template control
Representative questions ask for the seven Serializer results, an illegal function partial specialisation, the winning route overload, the type from Box{"kg"}, or a repair for ambiguous class partials.
For route(nullptr), the exact type is std::nullptr_t; T* deduction fails; the by-value primary deduces T=std::nullptr_t. Answer: "generic".
For Serializer<bool*>, the template-id matches T* with T=bool. A non-null pointer returns "pointer", not "true" or "false".
8. C++ template specialisation: the short version and next step
Recall four steps: identify class or function template; write the exact type; deduce; then apply class-specialisation ordering or overload resolution. If the path is unclear, simplify the design.
Reproduce the Serializer matrix and explain why Serializer<const bool> uses the primary. Then predict route(42), route(&n), route(text), and route(nullptr): "generic", "pointer-template", "text-overload", and "generic".
Use C++ Programming for focused language study, or Coding For Placements for wider coding practice. Always write the candidates and exact types before asking the compiler.
Keep learning
Templates in C++: Reusable Parameterized Code with Worked Examples
Learn how one C++ template produces type-specific functions and classes. Follow exact calls, calculations, deduction failures, and practical corrections.
Friend Functions in C++: Controlled Class Access with a Worked Example
See how a non-member friend function can validate and update two Wallet objects while private balances stay hidden from ordinary callers. Includes runnable C++ and an exact trace.
Data Types in C++: Fundamental and Derived Types with Worked Examples
Learn how C++ fundamental types differ from arrays, pointers, references, and function types. Trace one complete program, then test the conversion traps.
C++ Constructors and Object Initialization: Techniques, Order and a Worked Trace
Learn how C++ chooses default, parameterised, delegating and copy constructors, then trace member states and initialization order through complete code.





