3. Template type deduction when template parameter is neither a pointer nor a reference

Motivation for writing, this post is, In the past I interviewed many candidates for C++ and this was one of the topics where I found candidates getting confused with templates type deduction when template parameter is neither a pointer nor a reference.

What is a template?

As a quick short introduction, In General template, is a ‘pattern’ that is used to create specific instances. Moving from generics to specifics. This means you can generate specific instances by repeating the same pattern again and again for different types of inputs provided. For example, below we have the same presentation template used for C++ and Python-specific presentations for different audiences.



C++ Templates


In C++ a template is a class or a function that we parameterize with a set of types or values. We use templates to represent concepts that are best understood as something very general from which we can generate specific types and functions by specifying arguments, such as the element type double. Today we will be taking the functions template for this discussion. For example, we can think of a function template as looking like this:


template<typename T>
void f(ParamType param);

A call can look like this:

f(expr); // call f with some expression

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 ina 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:

  1. if expr’s type is a reference, ignore the reference part.
  2. 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:

  1. 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.
  2. 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 const to the left of the asterisk says that what ptr points tothe character stringis const, hence can’t be modified. When ptr is passed to func, the contents making up the pointer are copied into param.

That’s means, the pointer itself (ptr) will be passed by value.

So as per the type deduction rule for by-value parameters, the constness of ptr will be ignored, and the type deduced for param will be const char*, i.e., a modifiable pointer to a const character string.
 
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.

Make sense isn't it?
 

I regularly post the Modern C++ quiz on  QickStep follow this for a regular Modern C++ quiz to dazzle your mind 😊.  

Comments

Popular posts from this blog

1. nullptr C++

2. Why should someone Learn C++ ?