What is C++ reference collapsing, how it works?

Share the Article

We will check universal reference and then we will understand c++ reference collapsing. Universal References is a special concept. Here the reference occurs in two contexts. In both places, the type deduction is happens.

Function template parameters

template <typename T> void func (T&& param);

In this example, func is a template function. On calling “func”, the passed argument type will deduce the type of template parameter T. The Argument param is of a special type (T&&). Here, when an && sign follows template type T. This is a universal reference.

Auto declarations with &&

auto&& var2 = var1;

The type of var1 deduces the data type of auto variable var2. The variable var2 is of type auto&& and here this is a Universal reference.

Universal References are not R-Values

Universal reference is not r-value reference. In Universal reference, the type of parameter is template type. However, in r-value reference, the exact data type is clear. Like, in following example, the type of param is int&&.

void func(int&& param); int&& var2 = var1;

Universal reference has dual nature,

it can bind to either to L-value or R-value. (An R-value reference always bind to an R-value)

Once the type deduction happens with type of argument provided from calling function. The compiler then decides the status of universal references from type of argument. Means when program passes an L-value argument, then Universal reference will correspond to an L-value. Similarly when program passes an R-value argument, then template type corresponds to R-value type.

Reference Collapsing Rules

When the program passes an L-value or an R-value parameter to universal reference parameter, then to get final function signature, the compiler generates reference to reference expressions.

There are four possible reference-reference combinations

  1. lvalue to lvalue,
  2. lvalue to rvalue,
  3. rvalue to lvalue, and
  4. rvalue to rvalue

With these reference combinations, the compiler follows certain rules to collapse these references to references and generate them into a single reference. This is called c++ reference collapsing.

In simple terms, if either of the reference is an L-value then reference collapsing will lead to L-value finally. Otherwise, the reference collapsing will lead to R-value.

Reference Collapsing when program calls template function with Lvalue argument

template <typename T> void func (T&& param); //Function template with Universal reference int w; func(w); //calling the template with an L-value argument

Here, first T will deduce to type of passed argument. i.e., int&. (L-value)

This will create L-value reference to R-value reference in following statement:

func(int& && param)

As per rules, the final type will become L-value (from, L-value to R-value expression)

func(int & param);

Reference Collapsing when program calls template function with Rvalue argument

template <typename T> void func (T&& param); //Function template with Universal reference func(4) // calling the template with R-value

First, T will deduce to type of argument. i.e., int&& (R-value)

This will create R-value reference to R-value reference in following statement

func( int&& && param);

The final signature will become R-value (from R-value to R-value expression)

func(int&& param)

The underlying mechanism of these reference collapsing rules decides how std::forward does its work.

C++ Reference Collapsing happen in 4 contexts

  1. template instantiation
  2. auto type generation.
  3. The generation and use of typedefs
  4. Alias declarations

Above already explains scenarios, 1 and 2.

Typedef generation:

Compiler applies reference to reference collapsing rules when creating and evaluating typedef. The reference collapsing rules simply these reference and make a simple reference out of them.

For example, suppose we have a class template with an embedded typedef for && an rvalue reference type:

template <typename T> class Widget { public: typedef T&& typedef_rvalue_reference; };

When an L-value instantiates the class.

Widget<int&> w;

The typedef_rvalue_reference will resolve to L-value

typedef int& && typedef_rvalue_reference;

finally, (L-value to R-value expression)

typedef int& typedef_rvalue_reference;

Main Funda: Reference collapsing rules specify how the features like, forward<T> work

Related Topics:

 What are the drawbacks of using enum ?
What is an implicit interface in templates
Which member functions are generated by compiler in class?
How to stop compiler from generating special member functions?
Compiler Generated Destructor is always non-virtual
Order of constructors and destructors
How to make a class object un-copyable?
Why virtual functions should not be called in constructor & destructor ?
Explaining C++ casts
How pointer to class members are different ?
How std::forward( ) works?
Rule of Three
How std::move() function works?
What is reference collapsing?
How delete keyword can be used to filter polymorphism

Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *