Template argument deduction
Back to the cookie cutter from the previous lesson. Usually you do not say which dough is under it: the compiler looks at the call's arguments and learns the type from them. But put two different doughs under one cutter at once, and it cannot tell which one to cut.
An ordinary function double max_of(double a, double b) accepts the call max_of(1, 2.5): the argument 1 converts to double. A template with the same body rejects this call:
#include <iostream>
template <typename T>
T max_of(T a, T b) {
return a > b ? a : b;
}
int main() {
std::cout << max_of(1, 2.5) << '\n'; // ✗
}error: no matching function for call to 'max_of'Deduction from arguments
A call to a template usually does not name the type T: the compiler finds it itself. This is template argument deduction: the compiler compares the type of each argument with the type of its parameter. The literal 3 has the type int, so max_of(3, 7) gets T = int:
std::cout << max_of(3, 7) << ' ' << max_of(2.5, 1.5) << '\n'; // 7 2.5The compiler first deduces T and then instantiates the function for that type. The deduced type replaces T everywhere in the template, the return type included: max_of(2.5, 1.5) returns a double.
Each argument deduces T independently of the others, and the results must match. In max_of(1, 2.5), the first argument gives int and the second gives double. Conversions such as int → double are not applied during deduction, so the compiler cannot pick one type. The note under the error says so: deduced conflicting types for parameter 'T' ('int' vs. 'double').
Step through the call max_of(1, 2.5) and watch each argument give its own T, then compare it with max_of<double>(1, 2.5).
template <typename T> T max_of(T a, T b)
The deductions disagree: int vs. double. The template is discarded, no other function fits — a compile error.
Explicit template argument
The type can be given by hand, in angle brackets after the template's name. Such an explicit template argument replaces deduction. T is already known, and the arguments convert as in a call to an ordinary function. Another way is to pass arguments of one type: the literal 1.0 has the type double.
std::cout << max_of<double>(1, 2.5) << ' ' << max_of(1.0, 2.5) << '\n'; // 2.5 2.5An explicit template argument is also needed when the type of a value in the call is not yours to choose. For a string s, the call max_of(s.size(), 10) does not compile: size() returns std::size_t, and 10 has the type int. The call max_of<std::size_t>(s.size(), 10) compiles and returns the larger of the two numbers.
Common mistake
The parameter T appears only in the return type, and the programmer expects the variable left of = to supply the type:
template <typename T>
T zero() {
return T{};
}
int main() {
double d = zero(); // ✗
}error: no matching function for call to 'zero'T{} creates a default value of type T, as int x{} does: for numbers it is 0. But zero() has no arguments, and the type of the variable d takes no part in deduction. That is why the compiler adds: couldn't infer template argument 'T'. Give the type explicitly: zero<double>().
Key points
- The compiler deduces
Tfrom the types of the arguments, each argument on its own. - Deduction applies no conversions:
max_of(1, 2.5)does not compile. - The explicit template argument in
max_of<double>(1, 2.5)replaces deduction, and the arguments convert. - The type of the variable that receives the result takes no part in deduction.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.