Search code examples
c++c++11referenceforwarding-reference

C++ Universal Reference and type deduction of LRef parameter


Assume we have some code

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

int x = 5;
// deduced Foo<int&>(int&).
// Argument type A = int. T = int&.
Foo(x);     

int& rx = x;
// deduced Foo<int&>(int& &&) -> Foo<int&>(int&).
// But... argument type A = int&, an lvalue reference to int.
// Is T = int& or under the hood it is T = int& &?
Foo(rx);

According to https://en.cppreference.com/w/cpp/language/template_argument_deduction

4) If P is an rvalue reference to a cv-unqualified template parameter (so-called forwarding reference), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction

What I want to know: is 'reference collapsing' rules are applied to deduced type T (not to P, parameter type)?

So do we really have for 'Foo(rx)' T = int& &, which collapsed to T = int&?


Solution

  • Note that for practically all technical purposes, the type of an expression is never considered to be a reference type. [expr.type]/1:

    If an expression initially has the type "reference to T" ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis.

    The one notable exception is when decltype() is applied to an unparenthesized name or class member access expression. Although the syntax considers the name to be an expression, the semantic result involves the declared type of the name instead of the expression's type and value category.

    So in your example, x is an lvalue of type int, and rx is an lvalue of type int. After their declarations and initializations, the language makes no distinction at all between them, except again if you use decltype(x) or decltype(rx).

    This means that template type deduction for Foo(rx) works exactly the same as for Foo(x): The type A is int, not int&, and the argument is an lvalue. By the rule you quoted, T is deduced to be int&, and when substituting T=int& into the function type, the reference-collapsing rule says that T&& is int&.