Function Template Argument Deduction
Develop a thorough understanding of function template argument deduction.
We'll cover the following...
We have briefly talked about the fact that the compiler can sometimes deduce the template arguments from the context of the function call, allowing us to avoid explicitly specifying them. The rules for template argument deduction are more complex, and we’ll explore this topic in this lesson.
Let’s start the discussion by looking at a simple example:
template<typename T>void process(T arg){std::cout << "process " << arg << ", type id: " << typeid(arg).name() << '\n';}int main() {process(42); // [1] T is intprocess<int>(42); // [2] T is int, redundantprocess<short>(42); // [3] T is short}
Note: The
i
ands
in the output above representint
andshort
, respectively.
In this snippet, process
is a function template with a single type template parameter. The calls process(42)
and process<int>(42)
are identical because, in the first case, the compiler is able to deduce the type of the type template parameter T
as int
from the value of the argument passed to the function.
Rules for deducing template arguments
When the compiler tries to deduce the template arguments, it performs the matching of the types of the template parameters with the types of the arguments used to invoke the function. There are some rules that govern this matching. The compiler can match the following:
Types (both cv-qualified and nonqualified) of the form
T
,T const
, andT volatile
:
struct account_t{int number;};template<typename T>void process01(T) { std::cout << "T\n"; }template<typename T>void process02(T const) { std::cout << "T const\n"; }template<typename T>void process03(T volatile) { std::cout << "T volatile\n";}int main() {account_t ac{ 42 };process01(ac); // Tprocess02(ac); // T constprocess03(ac); // T volatile}
Pointers (
T*
), l-value references (T&
), and r-value references (T&&
):
template<typename T>void process04(T*) { std::cout << "T*\n"; }template<typename T>void process04(T&) { std::cout << "T&\n"; }template<typename T>void process05(T&&) { std::cout << "T&&\n"; }int main() {account_t ac{ 42 };process04(&ac); // T*process04(ac); // T&process05(ac); // T&&}
Arrays such as
T[5]
, orC[5][n]
, whereC
is a class type andn
is a non-type template argument:
template<typename T>void process06(T[5]) { std::cout << "T[5]\n"; }template<size_t n>void process07(account_t[5][n]){ std::cout << "C[5][n]\n"; }int main() {account_t arr1[5] {};process06(arr1); // T[5]account_t ac{ 42 };process06(&ac); // T[5]account_t arr2[5][3];process07(arr2); // C[5][n]}