Search code examples
c++functiontemplatestype-deductionforwarding-reference

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& ?

Solution

  • 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 &.