3. Template type deduction when template parameter is neither a pointer nor a reference
What is a template?
C++ Templates
During compilation, compilers use expr to
deduce two types: one for T and one for ParamType.
These types are frequently different, because
ParamType often contains e.g., const or reference qualifiers. For example, if the template is declared like this:
template<typename T>
void f(const T& param);
and we have a call like this,
int x = 0;
f(x); // call f with an int
T is deduced to be int, but ParamType is deduced to be const int&.
It’s natural to expect that the type deduced for T is the same as the type of the argument passed to the function, i.e., that T is the type of expr. In the above example, That’s the case: x is an int, and T is deduced to be int. But it doesn’t always work that way. The type deduced for T is dependent not just on the type of expr, but also on the form of ParamType. So, it’s become really important to understand the template type deduction rules.
I will be focusing on the case where ParamType
is neither a pointer nor a reference it's quite confusing.
When ParamType is neither a pointer nor a reference, the first thing we should always keep in mind that we’re dealing with pass-by-value.
template<typename T>
void func(T param); // param is now passed by value
This means that param will be a copy of whatever is passed in—a completely new object. The fact that param will be a newly copied object motivates the rules that govern how T is deduced from expr and the following are the rules:
- if expr’s type is a reference, ignore the reference part.
- if, after ignoring expr’s reference-ness, expr is const, ignore that, too. If it's volatile, also ignore that.
Hence in our example:
int var = 30;
const int cvar = var;
const int& rvar = var;
func(var); //
T's and param's types are both int
func(cvar); //
T's and param's types are again both int
func(rvar); //
T's and param's types are still both int
Wondering why func(cvar) and func(rvar) cases T and param type are int?
Let’s try to uncover the mystery here:
- Note that even though cvar and rvar represent const values, param isn’t const. That makes sense as param is an object that’s completely independent of cvar and rvar— it's a copy of cvar or rvar.
- The fact that cvar and rvar can’t be modified says nothing about whether their copies or param can be modified. That’s why expr’s constness (and volatileness, if any) is ignored when deducing a type for param: just because expr can’t be modified doesn’t mean that a copy of it can’t be.
It’s important to understand const (and volatile) is ignored only for by-value parameters.
Let’s consider the case where expr is a const
pointer to a const object and expr is passed to a by-value param:
template<typename T>
void func(T param){}
// param is still passed by value
const char* const ptr = "Are templates
trick?"; // ptr is const pointer to const object
func(ptr); // pass arg of type const char * const
Here, the ptr and the object it's pointing both are constant,
The constness of what ptr points to is preserved during type deduction, but the constness of ptr itself is ignored when copying it to create the new pointer, param.
Comments
Post a Comment