Template Arguments & Type Deduction
Modern C++ C++11/17/20 Core Mechanics
Understanding how the compiler deduces template arguments is crucial for writing robust, bug-free generic code. The rules for template type deduction do not only apply to function templates (C++98) but also to auto (C++11), class templates (C++17), and concepts (C++20). {: .fs-5 .fw-300 }
Table of Contents
1. The Three Rules of Type Deduction
When deducing the template type, three entities come into play: T, ParameterType, and the expression passed to the function. The compiler deduces two types: the actual template parameter T and the fully resolved ParameterType. The behavior changes dramatically based on how the parameter is declared:
- Pass by Value: The deduced type ignores reference qualifiers (
&). Furthermore, if the expression isconstorvolatile, those qualifiers are also ignored. - Pass by Reference/Pointer: The reference or pointer is added to the deduced type. Crucially, the
constnessorvolatilenessof the original expression is respected and preserved. - Pass by Universal Reference (
&&): When the expression is an lvalue, the resulting type becomes an lvalue reference. When it is an rvalue, it becomes an rvalue reference.
2. Why This Matters (Practical Examples)
Understanding these rules prevents dangerous bugs related to accidental copying, lost const safety, and unexpected array behavior. Because auto type deduction uses the exact same rules as template type deduction, mastering this concept is mandatory for modern C++ development.
Code Example: Pitfalls and Solutions
template <typename T> void passByValue(T param);
template <typename T> void passByRef(T& param);
int main() {
const int myData = 42;
int myArr[5] = {1, 2, 3, 4, 5};
// --- 1. Losing Const Safety ---
passByValue(myData); // T deduced as 'int'. Const is stripped!
passByRef(myData); // T deduced as 'const int'. Const is preserved!
// --- 2. Unexpected Array Decay ---
// When passed by value, implicit array-to-pointer conversion is applied.
passByValue(myArr); // T deduced as 'int*'. Array decays!
// When passed by reference, size information is retained.
passByRef(myArr); // T deduced as 'int[5]'. No decay!
// --- 3. The 'auto' Connection ---
// Regard 'auto' as the replacement for T.
auto val = myData; // val is 'int' (Const stripped)
auto& ref = myData; // ref is 'const int&' (Const preserved)
}