AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content

12 Templates and generic programming / Lesson 1 of 6

Function templates — one definition, every type

14 min · +10 XP · 2 exercises · 7 questions

Read the text14 min
Exercises0/2
Quiz7 q

Quick answer

What is a function template in C++?

A function template is a function written with type parameters, such as template <typename T> T max_of(const T& a, const T& b). It is a recipe, not a function: each time it is called with new types, the compiler deduces T from the arguments and instantiates a separate function, so max_of(3, 9) creates max_of<int>. std::sort, std::swap and std::max are function templates.

A function fixes the types of its parameters when it is written; a function template takes the types as parameters too, and the compiler writes the function you need the first time you call it. std::max, std::swap, std::sort and std::make_unique are all function templates, which is why one std::sort sorts ints, std::strings and your own types without a line of dispatch. This lesson settles how a template is declared, how the compiler deduces T from a call, when you must spell the argument out, how a template coexists with ordinary overloads, and why a template lives in a header rather than a .cpp file.

A function that is a recipe

#include <iostream>
#include <string>

template <typename T>
T max_of(const T& a, const T& b) {
    return b < a ? a : b;
}

int main() {
    std::cout << max_of(3, 9) << '\n';                                       // T = int
    std::cout << max_of(2.5, -1.0) << '\n';                                  // T = double
    std::cout << max_of(std::string("pear"), std::string("apple")) << '\n';  // T = std::string
    return 0;
}

template <typename T> introduces a template parameter: a placeholder type named T that the rest of the declaration may use as if it were a real type. typename and class mean the same thing in this position; older code writes class. The name max_of is not a function — it is a recipe, and max_of<int>, max_of<double> and max_of<std::string> are the three functions the compiler generated from it above. Generating one is called instantiation, and it happens on demand: a template that is never called produces no code, and a template that is called with four types produces four functions.

The body uses only <, not > or ==. That is deliberate and it is the standard library's convention: the fewer operations the body needs, the more types can use it. A type that defines only operator< works with std::sort, std::set and this max_of alike.

Deduction: the compiler reads the arguments

The call max_of(3, 9) never mentions int. The compiler matches each parameter's pattern (const T&) against the argument's type (int) and solves for T. The rules mirror auto from Module 2, lesson 5: a by-value T drops references and top-level const, and a const T& binds to anything and deduces the referenced type. Every parameter that mentions T must agree:

error: no matching function for call to 'max_of(int, double)'
note: candidate: 'template<class T> T max_of(const T&, const T&)'
note:   deduced conflicting types for parameter 'T' ('int' and 'double')

max_of(3, 2.5) is the call that produced that. Nothing is converted during deduction — the compiler does not promote the 3 to double the way it would for a plain double max_of(double, double) — so the two deductions disagree and the call fails. The fixes are to convert one argument yourself or to name T explicitly, next.

The trap that compiles is the string literal. max_of("pear", "apple") deduces T = const char*, and b < a then compares two addresses, not two words: it returns whichever literal the linker placed later in memory. Pass std::string values, or write "pear"s with using namespace std::literals;.

Explicit template arguments

Angle brackets after the name set the parameters by hand, and deduction is skipped for the ones you name:

max_of<double>(3, 2.5);   // T = double; the 3 converts to 3.0 like any argument

template <typename T>
T read_one() {            // nothing to deduce from: T appears only in the return type
    T value;
    std::cin >> value;
    return value;
}
int n = read_one<int>();

template <typename To, typename From>
To convert(const From& from) { return static_cast<To>(from); }
double d = convert<double>(7);   // To named, From deduced as int

Explicit arguments fill the parameter list from the left; anything after them is still deduced. That is why To comes before From in convert — the caller names the one that cannot be deduced and lets the argument supply the other — and it is exactly how std::make_unique<Widget>(a, b) names the type to build while deducing the constructor arguments.

More than one type parameter

template <typename A, typename B>
auto add(const A& a, const B& b) {
    return a + b;               // return type deduced from the return statement (C++14)
}
add(3, 2.5);                    // A = int, B = double, returns double

Independent parameters deduce independently, so mixed types are fine here where max_of refused them. With auto as the return type the compiler takes the type of the return expression; every return in the body must then agree, or the function does not compile. When the return type must be spelled from the parameters, the trailing form auto add(const A& a, const B& b) -> decltype(a + b) says so (lesson 6 covers decltype).

Templates and overloads together

A template can share its name with ordinary functions, and the compiler chooses among all of them:

void describe(int) { std::cout << "int overload\n"; }

template <typename T>
void describe(const T&) { std::cout << "template\n"; }

describe(5);      // int overload: a non-template that matches exactly wins
describe(5L);     // template, T = long: an exact template match beats a conversion
describe(2.5);    // template, T = double

The rule is short: deduce the template, then run ordinary overload resolution over every candidate; when a non-template and a template instantiation are equally good, the non-template wins. That tie-break is what lets you handle one awkward type — const char*, say — with a plain overload while the template covers everything else, and it is the recommended tool for the job; lesson 3 explains why specialising a function template is the wrong one.

Abbreviated function templates (C++20)

When you never need to name T, the auto parameter of a generic lambda (Module 4, lesson 5) is available on an ordinary function too:

void show(const auto& value) {          // template <typename T> void show(const T&)
    std::cout << value << '\n';
}

auto add(auto a, auto b) { return a + b; }   // two parameters: template <typename A, typename B>

Each auto is its own template parameter, so add(auto, auto) accepts mixed types. The long form is still required when the body needs T — to declare a local T sum{}, or to write std::vector<T> — and lesson 5 adds a concept in front of the auto to say what the type must support.

A template lives in a header

The compiler generates max_of<int> where it sees the call, so the whole definition — not just a declaration — must be visible in that translation unit. Put the template's body in the header. A declaration in max.h with the definition in max.cpp compiles both files and fails at link time with undefined reference to 'int max_of<int>(int const&, int const&)', because nothing in max.cpp asked for the int version and so it was never generated. The one-definition rule (Module 1, lesson 2) permits identical template definitions in many translation units; the linker keeps one copy of each instantiation.

Pitfalls

MistakeWhat happens
max_of(3, 2.5)Deduction conflict: int vs double. Name T or convert one argument.
max_of("pear", "apple")T = const char*; compares addresses, compiles, wrong.
read_one() without <int>T appears only in the return type: nothing to deduce from.
Definition in a .cpp, declaration in the headerUndefined reference at link time.
auto return with return 1; in one branch and return 2.5; in anotherInconsistent deduction: compile error.
Trusting an uncalled templateOnly its syntax is checked until something instantiates it.

Key takeaways

  • A function template is a recipe; each set of template arguments instantiates a separate function, on demand.
  • Deduction matches parameter patterns to argument types and never converts: mismatched arguments to one T fail.
  • f<double>(…) names the arguments explicitly, filling from the left; it is the only way when T is not deducible.
  • Non-template overloads win exact-match ties; use an overload, not a specialisation, for the special case.
  • void f(const auto& x) is an abbreviated template; each auto is its own parameter.
  • Templates go in headers, definition and all.

Common questions

Why must C++ templates be defined in header files?

The compiler generates max_of<int> where it sees the call, so the complete definition must be visible in that translation unit. With only a declaration in the header and the body in a .cpp file, nothing instantiates the version you need and the link fails with an undefined reference. The one-definition rule allows the same template definition in many files.

Why does max_of(3, 2.5) fail with deduced conflicting types?

Template argument deduction never converts arguments. The first argument deduces T = int and the second T = double, and the two deductions conflict. Either convert one argument yourself or name the type explicitly, as in max_of<double>(3, 2.5), which skips deduction so the 3 converts to 3.0 like any argument.

What is the difference between typename and class in a template parameter?

None: in template <typename T> and template <class T> the two keywords mean exactly the same thing, and T may be any type, not only a class. Older code tends to write class. The keywords differ only elsewhere — typename is also what marks a dependent name as a type.

Does a non-template overload beat a function template in C++?

When both match equally well, yes: overload resolution considers the non-template functions and the deduced template instantiations together, and an exact non-template match wins the tie. A template can still win when it matches better, as when describe(5L) picks the template over a describe(int) that would need a conversion.

What is an abbreviated function template in C++20?

A function with an auto parameter, such as void show(const auto& value), which is shorthand for template <typename T> void show(const T& value). Each auto is its own template parameter, so add(auto a, auto b) accepts mixed types. Use the long form when the body needs to name the type.

Mark as readSign in to keep your place.

C++ Function Templates Explained: Deduction and Headers

A C++ function template is a recipe the compiler instantiates per type. Template argument deduction, explicit arguments and why templates go in headers.

  • Course: C++ study plan
  • Module: Templates and generic programming
  • Kind: Lesson
  • Reading time: 14 min
  • Runtime: Clang 18 · C++20

What is a function template in C++?

A function template is a function written with type parameters, such as template <typename T> T max_of(const T& a, const T& b). It is a recipe, not a function: each time it is called with new types, the compiler deduces T from the arguments and instantiates a separate function, so max_of(3, 9) creates max_of<int>. std::sort, std::swap and std::max are function templates.

Lesson

A function fixes the types of its parameters when it is written; a function template takes the types as parameters too, and the compiler writes the function you need the first time you call it. std::max, std::swap, std::sort and std::make_unique are all function templates, which is why one std::sort sorts ints, std::strings and your own types without a line of dispatch. This lesson settles how a template is declared, how the compiler deduces T from a call, when you must spell the argument out, how a template coexists with ordinary overloads, and why a template lives in a header rather than a .cpp file.

A function that is a recipe

#include <iostream>
#include <string>

template <typename T>
T max_of(const T& a, const T& b) {
    return b < a ? a : b;
}

int main() {
    std::cout << max_of(3, 9) << '\n';                                       // T = int
    std::cout << max_of(2.5, -1.0) << '\n';                                  // T = double
    std::cout << max_of(std::string("pear"), std::string("apple")) << '\n';  // T = std::string
    return 0;
}

template <typename T> introduces a template parameter: a placeholder type named T that the rest of the declaration may use as if it were a real type. typename and class mean the same thing in this position; older code writes class. The name max_of is not a function — it is a recipe, and max_of<int>, max_of<double> and max_of<std::string> are the three functions the compiler generated from it above. Generating one is called instantiation, and it happens on demand: a template that is never called produces no code, and a template that is called with four types produces four functions.

The body uses only <, not > or ==. That is deliberate and it is the standard library's convention: the fewer operations the body needs, the more types can use it. A type that defines only operator< works with std::sort, std::set and this max_of alike.

Deduction: the compiler reads the arguments

The call max_of(3, 9) never mentions int. The compiler matches each parameter's pattern (const T&) against the argument's type (int) and solves for T. The rules mirror auto from Module 2, lesson 5: a by-value T drops references and top-level const, and a const T& binds to anything and deduces the referenced type. Every parameter that mentions T must agree:

error: no matching function for call to 'max_of(int, double)'
note: candidate: 'template<class T> T max_of(const T&, const T&)'
note:   deduced conflicting types for parameter 'T' ('int' and 'double')

max_of(3, 2.5) is the call that produced that. Nothing is converted during deduction — the compiler does not promote the 3 to double the way it would for a plain double max_of(double, double) — so the two deductions disagree and the call fails. The fixes are to convert one argument yourself or to name T explicitly, next.

The trap that compiles is the string literal. max_of("pear", "apple") deduces T = const char*, and b < a then compares two addresses, not two words: it returns whichever literal the linker placed later in memory. Pass std::string values, or write "pear"s with using namespace std::literals;.

Explicit template arguments

Angle brackets after the name set the parameters by hand, and deduction is skipped for the ones you name:

max_of<double>(3, 2.5);   // T = double; the 3 converts to 3.0 like any argument

template <typename T>
T read_one() {            // nothing to deduce from: T appears only in the return type
    T value;
    std::cin >> value;
    return value;
}
int n = read_one<int>();

template <typename To, typename From>
To convert(const From& from) { return static_cast<To>(from); }
double d = convert<double>(7);   // To named, From deduced as int

Explicit arguments fill the parameter list from the left; anything after them is still deduced. That is why To comes before From in convert — the caller names the one that cannot be deduced and lets the argument supply the other — and it is exactly how std::make_unique<Widget>(a, b) names the type to build while deducing the constructor arguments.

More than one type parameter

template <typename A, typename B>
auto add(const A& a, const B& b) {
    return a + b;               // return type deduced from the return statement (C++14)
}
add(3, 2.5);                    // A = int, B = double, returns double

Independent parameters deduce independently, so mixed types are fine here where max_of refused them. With auto as the return type the compiler takes the type of the return expression; every return in the body must then agree, or the function does not compile. When the return type must be spelled from the parameters, the trailing form auto add(const A& a, const B& b) -> decltype(a + b) says so (lesson 6 covers decltype).

Templates and overloads together

A template can share its name with ordinary functions, and the compiler chooses among all of them:

void describe(int) { std::cout << "int overload\n"; }

template <typename T>
void describe(const T&) { std::cout << "template\n"; }

describe(5);      // int overload: a non-template that matches exactly wins
describe(5L);     // template, T = long: an exact template match beats a conversion
describe(2.5);    // template, T = double

The rule is short: deduce the template, then run ordinary overload resolution over every candidate; when a non-template and a template instantiation are equally good, the non-template wins. That tie-break is what lets you handle one awkward type — const char*, say — with a plain overload while the template covers everything else, and it is the recommended tool for the job; lesson 3 explains why specialising a function template is the wrong one.

Abbreviated function templates (C++20)

When you never need to name T, the auto parameter of a generic lambda (Module 4, lesson 5) is available on an ordinary function too:

void show(const auto& value) {          // template <typename T> void show(const T&)
    std::cout << value << '\n';
}

auto add(auto a, auto b) { return a + b; }   // two parameters: template <typename A, typename B>

Each auto is its own template parameter, so add(auto, auto) accepts mixed types. The long form is still required when the body needs T — to declare a local T sum{}, or to write std::vector<T> — and lesson 5 adds a concept in front of the auto to say what the type must support.

A template lives in a header

The compiler generates max_of<int> where it sees the call, so the whole definition — not just a declaration — must be visible in that translation unit. Put the template's body in the header. A declaration in max.h with the definition in max.cpp compiles both files and fails at link time with undefined reference to 'int max_of<int>(int const&, int const&)', because nothing in max.cpp asked for the int version and so it was never generated. The one-definition rule (Module 1, lesson 2) permits identical template definitions in many translation units; the linker keeps one copy of each instantiation.

Pitfalls

MistakeWhat happens
max_of(3, 2.5)Deduction conflict: int vs double. Name T or convert one argument.
max_of("pear", "apple")T = const char*; compares addresses, compiles, wrong.
read_one() without <int>T appears only in the return type: nothing to deduce from.
Definition in a .cpp, declaration in the headerUndefined reference at link time.
auto return with return 1; in one branch and return 2.5; in anotherInconsistent deduction: compile error.
Trusting an uncalled templateOnly its syntax is checked until something instantiates it.

Key takeaways

  • A function template is a recipe; each set of template arguments instantiates a separate function, on demand.
  • Deduction matches parameter patterns to argument types and never converts: mismatched arguments to one T fail.
  • f<double>(…) names the arguments explicitly, filling from the left; it is the only way when T is not deducible.
  • Non-template overloads win exact-match ties; use an overload, not a specialisation, for the special case.
  • void f(const auto& x) is an abbreviated template; each auto is its own parameter.
  • Templates go in headers, definition and all.

Common questions

Why must C++ templates be defined in header files?

The compiler generates max_of<int> where it sees the call, so the complete definition must be visible in that translation unit. With only a declaration in the header and the body in a .cpp file, nothing instantiates the version you need and the link fails with an undefined reference. The one-definition rule allows the same template definition in many files.

Why does max_of(3, 2.5) fail with deduced conflicting types?

Template argument deduction never converts arguments. The first argument deduces T = int and the second T = double, and the two deductions conflict. Either convert one argument yourself or name the type explicitly, as in max_of<double>(3, 2.5), which skips deduction so the 3 converts to 3.0 like any argument.

What is the difference between typename and class in a template parameter?

None: in template <typename T> and template <class T> the two keywords mean exactly the same thing, and T may be any type, not only a class. Older code tends to write class. The keywords differ only elsewhere — typename is also what marks a dependent name as a type.

Does a non-template overload beat a function template in C++?

When both match equally well, yes: overload resolution considers the non-template functions and the deduced template instantiations together, and an exact non-template match wins the tie. A template can still win when it matches better, as when describe(5L) picks the template over a describe(int) that would need a conversion.

What is an abbreviated function template in C++20?

A function with an auto parameter, such as void show(const auto& value), which is shorthand for template <typename T> void show(const T& value). Each auto is its own template parameter, so add(auto a, auto b) accepts mixed types. Use the long form when the body needs to name the type.

Exercises

max_of and min_of over int, double and std::string

Write two function templates, max_of(const T& a, const T& b) and min_of(const T& a, const T& b), each using only operator< in its body (so any type that defines < works). Read an integer n, then n lines of <type> <a> <b> where <type> is int, double or string. Both values on a line are read as that type, so one instantiation serves the line, and the three type words instantiate the templates three times in total.

Print <type>: min=<min> max=<max> per line. Doubles print with two decimals (std::fixed and std::setprecision(2)); strings compare the way std::string does, so Zebra is less than apple. Put the reading in a helper such as answer<T>(type) — T appears only in its body, so the call must name it: answer<int>(type).

Input: n, then n lines of type a b. Output: n lines.

3
int 3 9
double 2.5 -1
string pear apple

prints

int: min=3 max=9
double: min=-1.00 max=2.50
string: min=apple max=pear

clamp_to with read_one<T>() and a printing overload

Write three things. read_one<T>() reads one value of type T from standard input and returns it — T appears only in the return type, so every call names it explicitly. clamp_to(const T& v, const T& lo, const T& hi) returns lo when v < lo, hi when hi < v, and v otherwise. And two printers that share a name: a plain function void show(double v) that prints with two decimals, and an abbreviated function template void show(const auto& v) that prints the value as it is. Overload resolution sends a double to the plain function (an exact non-template match wins the tie) and an int or a std::string to the template (an exact template match beats the conversion to double).

Read n, then n lines of <type> <value> <lo> <hi> with <type> one of int, double, string. For each line print <type>: <clamped>. After the last line print changed=<k>, the number of lines whose value was outside [lo, hi].

Input: n, then n lines of type value lo hi. Output: n lines, then changed=<k>.

3
int 15 0 10
double 2.5 1 5
string m a k

prints

int: 10
double: 2.50
string: k
changed=2

In this module: Templates and generic programming

← Checkpoint — Operator overloading · Class templates — Stack<T>, Pair<A, B> and deduction →