Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding type deduction for universal references

Let foo be the function:

template< typename T >
void foo( T&& a ){}

To what type will T be deduced for the following calls of foo:

foo( 0 ); // is T here int or int&& ?

int a = 0;
foo( a ); // is T here int or int& ?
like image 544
abraham_hilbert Avatar asked Oct 04 '17 08:10

abraham_hilbert


2 Answers

The default rule for type deduction is that reference types can never be the result of deduction. Given this code,

template <class T>
void bar(T par);

bar(0);
int a;
bar(a);
int &b;
bar(b);

all 3 calls will call foo<int>. That is, T is deduced to int and par is of type int.

Forwarding references work by simple addition of one rule: when the argument used for type deduction of a forwarding reference (i.e. of a parameter T&& for a deduced T) is an lvalue of type X, the type X & is used instead of X for deduction.

Note that this means that given a type X, only X or X & can ever be the result of type deduction; X && never can.

Let's analyse your code (I will rename he function parameter, to make it clear what I'm referring to):

template <class T>
void foo(T &&par);

foo(0);

int a;
foo(a);

In the first case foo(0), the argument is an rvalue of type int The type int is therefore used for type deduction, meaning that T is deduced to int (the function called is foo<int>) and the type of par is int &&.

In the second case foo(a), the argument is an lvalue of type int. Forwarding reference rule kicks in and the type int & is used for deduction. T is therefore deduced to int & (the function called is foo<int&>), and the type of par is "int & &&", which collapses to int &.

like image 67
Angew is no longer proud of SO Avatar answered Sep 21 '22 15:09

Angew is no longer proud of SO


The expression T&& in a deduced context like what you provided

template< typename T >
void foo( T&& a ){}

ie T is deduced based on the provided argument, is subject to reference collapsing rules.

In short;

  • if the provided argument is an lvalue of type type, T&& will expand to type& && that collapses to type&

  • if the provided argument is an rvalue of type type, T&& will expand to type && that collapses to type&&

Note that both are references, if you need to trigger the rvalue overload of another function, you need to do std::forward<T>(a)

like image 30
sp2danny Avatar answered Sep 23 '22 15:09

sp2danny