Templates

Template Argument Deduction

When a function template is invoked, the compiler is often able to deduce the template type by itself. That process is called template argument deduction. It allows for a function call to leave out the angle brackets naming the type.

Think about the familiar max template again.

#include <iostream>

template <typename T>
T max(T x, T y)
{
    // Choose between the two expressions using this condition
    return (x < y) ? y : x;
}

int main()
{
    std::cout << max(4, 9) << '\n';
    return 0;
}

The call is just written as max(4, 9). There are no angle brackets. The compiler looks at the two arguments and finds them both to be of type int and so deduces that the value of the template parameter T is int.

It then produces a concrete function int max(int, int) and compiles the call to this generated function.

The larger value is returned and printed.

While we've been able to get away with the call in the previous example, it can also be written with the explicit type.

#include <iostream>

// Make the following definition depend on template arguments
template <typename T>
T max(T x, T y)
{
    return (x < y) ? y : x;
}

int main()
{
    std::cout << max<int>(4, 9) << '\n';
    return 0;
}

The brackets insist to compile the use of int for T. The generated function is the same as the one returned by deduction, and the resulting print is also the same. The explicit specification becomes useful when deduction can't decide. However, the default is the brackets are not needed in the common case.

Deduction proceeds by identifying a correspondence between the types of the call arguments and the types in the template parameter list. In the pattern T max(T x, T y) the requirement is that both parameters have the same type T. If a call passes in two int arguments, the match will succeed with T int and if two double arguments are passed, T will become double and a second instantiation will be generated.

If types are not compatible, deduction cannot be performed and the following program will not compile:

#include <iostream>

template <typename T>
T max(T x, T y)
{
    return (x < y) ? y : x;
}

int main()
{
    std::cout << max(4, 9.5) << '\n';
    return 0;
}

The first argument is an int and the second a double. There is no type T that will satisfy both parameters, the compiler is unable to generate a function, and produces an error. Explicit specification resolves the ambiguity by choosing one type and then converting the other argument, but deduction never does this conversion.

The equivalence relation is the same for the one argument template.

#include <iostream>

template <typename T>
void print(T value)
{
    std::cout << value << '\n';
}

int main()
{
    print(42);
    print(3.14);
    print('Z');
    return 0;
}

The first call passes one argument each time. Since the first call takes int as argument types, the compiler deduces T as int and generates the function. Second and third calls deduce T as double and char respectively. We have three instantiations that print out its value then go to new line.

Deduction looks only at the types in the function parameter list. It doesn't look at the return type, or any other context outside of the call itself. If there is not enough information in the parameter list to deduce every template parameter, then deduction fails and you must explicitly provide a type.

With multiple template parameters, each one is inferred individually for each argument. This template has two type parameters:

#include <iostream>

template <typename T, typename U>
void show(T first, U second)
{
    std::cout << first << ' ' << second << '\n';
}

int main()
{
    show(5, 3.2);
    show('A', 100);
    return 0;
}

In the first call the two argument types are int and double. The compiler then assigns T as int and U as double, creating the function with two arguments as such. In the second call T becomes char and U becomes int. Since the two parameters are completely independent, there is no issue with mixed types.

Exercise: Template Argument DeductionStarter code, hints and solutionOpenClose
Guided practiceBeginner · 10–15 min

Practice: Template Argument Deduction

Work through the mission, reveal hints only when needed, and compare your result with the model solution.

Your challenge

Add print(true), identify its deduced type, and predict its default stream output.

Mission checklist

  1. Run the starter as a baseline, then predict the change requested in Your challenge.
  2. Make that change in your editor; use the lesson-specific hints if you need help.
  3. Compile and run your answer, then compare with the worked solution and explain the result.

Starter program

This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.

#include <iostream>

template <typename T>
void print(T value)
{
    std::cout << value << '\n';
}

int main()
{
    print(42);
    print(3.14);
    print('Z');
    return 0;
}

Hints

Hint 1

For print(T value), the argument determines T: the three shown calls use an integer, a double, and a character. The template body is checked for each selected type.

Hint 2

Try print(true) and identify its deduced type before predicting output. Template deduction chooses a type; it does not automatically change stream formatting to words.

Worked solution

The new print(true) call deduces T as bool. The stream still uses its default Boolean formatting, so the extra line is 1. Deduction selects a type; it does not automatically turn on boolalpha.

#include <iostream>

template <typename T>
void print(T value)
{
    std::cout << value << '\n';
}

int main()
{
    print(42);
    print(3.14);
    print('Z');
    print(true);
    return 0;
}

Example output

42
3.14
Z
1

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.