Skip to content

Instantly share code, notes, and snippets.

@MangaD
Created December 22, 2025 18:45
Show Gist options
  • Select an option

  • Save MangaD/0d5e076349c7b1d8b489f34c0e53ac70 to your computer and use it in GitHub Desktop.

Select an option

Save MangaD/0d5e076349c7b1d8b489f34c0e53ac70 to your computer and use it in GitHub Desktop.
Partial Ordering of Function Templates — How C++ Chooses the Most Specialized Match

Partial Ordering of Function Templates — How C++ Chooses the Most Specialized Match

CC0

Disclaimer: ChatGPT generated document.

In C++, partial ordering of function templates is the rule the compiler uses to decide which function template is more specialized when multiple templates could match the same call.

Think of it as the compiler asking:

“Given these two templates, which one is more specific for this set of arguments?”

If one template is more specialized, it wins overload resolution.


The problem partial ordering solves

Consider this:

template<typename T>
void f(T);

template<typename T>
void f(T*);

Now call it:

int x;
f(&x);

Both templates match:

  • f(T)T = int*
  • f(T*)T = int

Which one should be chosen?

Partial ordering answers this: 👉 f(T*) is more specialized than f(T).

So the call resolves to:

template<typename T>
void f(T*);   // chosen

Formal idea (simplified)

Given two function templates A and B:

  1. The compiler pretends:
    • “Can A be called with B’s parameter types?”
    • “Can B be called with A’s parameter types?”
  2. If A can match B, but B cannot match A, then A is more specialized
  3. The more specialized template is preferred

This comparison is pairwise, not global.


Simple example

template<typename T>
void g(T);

template<typename T>
void g(T&);

Call:

int x;
g(x);

Which one wins?

  • g(T) accepts anything
  • g(T&) only accepts lvalues

So:

g(T&) is more specialized ❌ g(T) is more general

Result:

template<typename T>
void g(T&);   // chosen

Multiple template parameters

template<typename T, typename U>
void h(T, U);

template<typename T>
void h(T, T);

Call:

h(1, 2);

Both match, but:

  • h(T, T) requires both arguments to be same type
  • h(T, U) allows any two types

So:

h(T, T) is more specialized 👉 it is selected


With pointers and const

template<typename T>
void p(T*);

template<typename T>
void p(const T*);

Call:

const int x = 0;
p(&x);

Which wins?

  • p(T*)T = const int
  • p(const T*)T = int

const T* is more constrained, so:

template<typename T>
void p(const T*);   // chosen

Partial ordering vs overload resolution

Important distinction:

  • Overload resolution decides between:
    • non-template functions
    • template functions
    • function templates vs non-templates
  • Partial ordering is only used when comparing function templates against each other

Example:

void f(int);          // non-template
template<typename T>
void f(T);

Call:

f(10);

➡️ Non-template wins before partial ordering even applies.


Where partial ordering applies

Partial ordering is used for:

  • Function templates
  • Member function templates
  • Overloaded operator templates
  • Deduction guides (similar logic)

It does not apply to:

  • Class templates (they use specialization rules instead)
  • Variable templates

Common pitfalls

Ambiguous templates

template<typename T>
void q(T, int);

template<typename T>
void q(int, T);

q(1, 1);   // ❌ ambiguous

Neither is more specialized — compilation error.


Accidental ambiguity with auto

template<typename T>
void r(T);

template<typename T>
void r(auto);

Both may appear equivalent — can easily lead to ambiguity.


Mental model (practical)

You can remember it like this:

The template that accepts fewer kinds of arguments is more specialized.

Or:

More constraints = higher priority


One-paragraph summary

Partial ordering of function templates is the C++ mechanism that ranks competing function templates by how specialized they are. When multiple templates can match a call, the compiler compares them pairwise and prefers the one that is more constrained and matches a narrower set of arguments. This prevents ambiguity and allows you to write clean overload sets using templates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment