Discussion: A Constant Struggle
Execute the code to understand the output and gain insights into template argument deduction.
We'll cover the following
Run the code
Now, it’s time to execute the code and observe the output.
#include <iostream>#include <type_traits>template <typename T>void byValue(T t){std::cout << std::is_const_v<T>; // true if T is const}template <typename T>void byReference(T &t){std::cout << std::is_const_v<T>; // true if T is const}int main(){int nonConstInt = 0;const int constInt = 0;byValue(nonConstInt);byValue(constInt);byReference(nonConstInt);byReference(constInt);}
Understanding the output
The two function templates byValue
and byReference
take their parameters by value and by reference, respectively. We then call these two function templates with a non-const
and a const int
as arguments. In which cases is T
deduced as const
?
Template parameter
Before we start digging into the deduction rules, let’s clarify two concepts that can be easily mixed up. The T
in template <typename T>
is called the template parameter. The goal of template argument deduction is to find T
. On the other hand, the T
and T &
in the function signatures byValue(T t)
and byReference(T &t)
are called the template parameter types. These are the actual parameter types of the function templates and are what we use for deduction.
Let’s refer to these template parameter types as P
to distinguish them from the template parameters T
. Here are three examples:
Template Parameter | Template Parameter Type P | |
|
|
|
|
|
|
|
|
|
As we can see from the examples, in both byValue
and byReference
, the template parameter is T
. In byValue
, the template parameter type P
is also just T
, but in byReference
, it is T &
.
The byValue
call
With that out of the way, let’s dig into the deduction rules. First, let’s look at the calls to byValue
:
template <typename T>void byValue(T t);int nonConstInt = 0;const int constInt = 0;byValue(nonConstInt);byValue(constInt);
Remember, the template parameter is T
, and the template parameter type P
is also just T
(as opposed to, for example, T &
).
When deducing the template parameter type P
, if it’s not a reference type, the deduction rules say to ignore any top-level cv-qualifiers (const
or volatile
) of the argument. Since P
is just T
, it’s not a reference type, and cv-qualifiers are ignored.
Wait, no matter if we pass a non-const
or a const
argument, we pretend it’s non-const
! Isn’t that a bit shady? It’s not. When P
is not a reference type, it means we pass the argument by value. It doesn’t matter whether the original argument was const
, the t
parameter inside byValue
is a copy, and we can’t modify the original argument.
Since top-level cv-qualifiers are ignored, we deduce P
from a non-const int
in both calls to byValue
and find P == int
in both cases. And since P == T
, T
is also non-const
. 0
is printed for both calls.
The byReferences
call
Next, let’s look at the calls to byReference
:
template <typename T>void byReference(T &t);int nonConstInt = 0;const int constInt = 0;byReference(nonConstInt);byReference(constInt);
The template parameter is T
, and the template parameter type P
is T &
.
This time, P
is a reference, and we do not ignore the top-level cv-qualifiers of the argument. In this case, that’s important! The reference parameter t
will bind to our original argument, so it’s important that we don’t try, for instance, to bind a non-const
reference to a const
argument.
When passed the non-const
nonConstInt
, P
is deduced as int &
, which means that T
is int
. It’s not const
, so 0
is printed.
When passed the const
constInt
, P
is deduced as const int &
, which means that T
is const int
. It is const
, so 1
is printed.
The complete rules for template argument deduction can be a bit daunting to read, but at least this part about ignoring top-level cv-qualifiers is intuitive enough to remember it.
Auto
The deduction rules for auto
are based on the rules for template argument deduction. So now that we know these template argument deduction rules, we also know the corresponding auto
deduction rules!
auto t1 = nonConstInt; // non-const objectauto t2 = constInt; // non-const objectauto &t3 = nonConstInt; // reference to non-const objectauto &t4 = constInt; // reference to const object
Attempt the following question to assess your understanding.
Consider the following code:
int x = 42;
const int y = x;
int &z = x;
auto a = y;
auto &b = z;
const auto c = z;
What is the type of a
?
int
const int
int&
const int&
Level up your interview prep. Join Educative to access 70+ hands-on prep courses.