简体   繁体   中英

Why an Rvalue Reference is Turned into Lvalue Reference by a Universal Reference

I suppose when a universal reference parameter is matched with an rvalue reference argument, an rvalue reference argument is returned. However, my testing shows that the rvalue reference is turned into a lvalue reference by the universal reference function template. Why is it so?

#include <iostream>
#include <type_traits>
using namespace std;
template <typename T>
T f1(T&&t) {  //<-----this is a universal reference
  cout << "is_lvalue reference:" << is_lvalue_reference<T>::value << endl;
  cout << "is_rvalue reference:" << is_rvalue_reference<T>::value << endl;
  cout << "is_reference:"        << is_reference<T>::value        << endl;
  return t;
}
void f2(int&& t) {
  cout << "f2 is_lvalue reference:" << is_lvalue_reference<decltype(t)>::value << endl;
  cout << "f2 is_rvalue reference:" << is_rvalue_reference<decltype(t)>::value << endl;
  cout << "f2 is_reference:" << is_reference<decltype(t)>::value << endl;
  f1(t);

}

int main()
{
  f2(5);
  return 0;
}

In both GCC and VC++2010, this is the result:

f2 is_lvalue reference:0
f2 is_rvalue reference:1
f2 is_reference:1
is_lvalue reference:1
is_rvalue reference:0
is_reference:1

In other words, the parameter t in f2 was an rvalue reference, but when passed to f1 , the parameter became a lvalue reference. Shouldn't it retain the rvalue-ness in f1 ?

The reason is that named rvalue references are treated as lvalues.

You should use std::move inside f2 when passing t to f1 to retain rvalueness:

void f2(int&& t) {
    f1(std::move(t));
}

Here you can find a good explanation.

Calling f1(t) , the argument is the expression t . Not static_cast<decltype(t)>(t) or something. Your examination of decltype(t) has nothing to do with the call of f1(t) .

The expression t has type int and value category lvalue . (A rule of thumb is that if you can take the address of an expression then it is an lvalue, and you can certainly write &t ). The "information" that a reference variable was originally declared as a reference is only visible via a decltype examination.

Since f1 is called with an lvalue, T is deduced to int& .

NB. You possibly want f1 to also use decltype(t) rather than T , if you ever want to see is_rvalue_reference being true in f1 . For rvalue arguments, T deduces to a non-reference type, eg if you fix f2 by making it do f1(std::move(t)); then f1 's T is int and decltype(t) in f1 is int&& .

After studying the C++11 standard, I have a vague idea on what was going on after my f1(t); in f2 . I describe it here to see if I got it right:

  1. In f2 , t is an lvalue of type int&& (not int , this is an important difference)
  2. the call f1(t); causes type to be deduced like this:

    2.1 when T in f1 is given an lvalue, it is deduced as a reference of that lvalue's type or int&& &

    2.2 reference collapsing causes int&& & to become int & . This is the value of T .

  3. Since the parameter of f1 is declared as T&& , the type of parameter t in f1 is int & && . So reference collapsing occurs a second time to deduce the type of t as int & .

  4. Hence T= int & and type of parameter t is int & . ie parameter t is an lvalue of type int &

Any comment?

use the std::forward to keep t as rvalue.

void f2(int&& t) {
  //....
  f1(std::forward<int>(t));
}

The t argument is passed as rvalue and deduced as type int. (see how the deduction is done here )

std::forward(t) returns a rvalue of type int&& so that calls f1(int&&)

without std::forward(t), f1(t) takes the argument of type int and calls f1(int&)

UPDATE: Please be aware of the difference between

 is_lvalue_reference<T> and is_lvalue_reference<decltype<t>>. 

In the template, T is deduced based on the function argument, whereas the value category of t is always lvalue in f2 and

forward<int>(t) and move(t) 

are always rvalue.

#include <iostream>
#include <type_traits>

class A {};


template <typename T>
T f0(T& t) {  //<-----this is a universal reference
    std::cout<< "f0 lvalue"<<'\n';

    // take T as template argument, it is type int, so not rvalue, not lvalue.
//  std::cout << "is_lvalue reference:" << std::is_lvalue_reference<T>::value << std::endl;
    std::cout << "is_lvalue reference:" << std::is_lvalue_reference<T>::value << std::endl;
    std::cout << "is_rvalue reference:" << std::is_rvalue_reference<T>::value << std::endl;
    std::cout << "is_reference:"        << std::is_reference<T>::value        << std::endl;
    return t;
}

template <typename T>
T f0(T&&t) {  //<-----this is a universal reference
    std::cout<< "f0 rvalue"<<'\n';
    std::cout << "is_lvalue reference:" << std::is_lvalue_reference<T>::value << std::endl;
    std::cout << "is_rvalue reference:" << std::is_rvalue_reference<T>::value << std::endl;
    std::cout << "is_reference:"        << std::is_reference<T>::value        << std::endl;
    return t;
}

template <typename T>
T f1(T& t) {  //<-----this is a universal reference
    std::cout<< "f1 lvalue"<<'\n';

    // take T as template argument, it is type int, so not rvalue, not lvalue.
//  std::cout << "is_lvalue reference:" << std::is_lvalue_reference<T>::value << std::endl;
    std::cout << "is_lvalue reference:" << std::is_lvalue_reference<decltype(t)>::value << std::endl;
    std::cout << "is_rvalue reference:" << std::is_rvalue_reference<decltype(t)>::value << std::endl;
    std::cout << "is_reference:"        << std::is_reference<decltype(t)>::value        << std::endl;
    return t;
}

template <typename T>
T f1(T&&t) {  //<-----this is a universal reference
    std::cout<< "f1 rvalue"<<'\n';
    std::cout << "is_lvalue reference:" << std::is_lvalue_reference<decltype(t)>::value << std::endl;
    std::cout << "is_rvalue reference:" << std::is_rvalue_reference<decltype(t)>::value << std::endl;
    std::cout << "is_reference:"        << std::is_reference<decltype(t)>::value        << std::endl;
    return t;
}

void f2(int&&t) {  //<-----this is a universal reference
    std::cout<< "f2 rvalue"<<'\n';
    std::cout << "is_lvalue reference:" << std::is_lvalue_reference<decltype(t)>::value << std::endl;
    std::cout << "is_rvalue reference:" << std::is_rvalue_reference<decltype(t)>::value << std::endl;
    std::cout << "is_reference:"        << std::is_reference<decltype(t)>::value        << std::endl;


    f1(std::forward<int>(t));   // T is deduced as int for f(T&&), type int is not rvalue, nor lvalue, t is rvalue
    f1(std::move(t));           // T is deduced as int for f(T&&), type int is not rvalue, nor lvalue, t is rvalue
    f1(t);                      // T is deduced as int for f(T&),  type int is not rvalue, nor lvalue, t is lvalue
                                //if f1(T&) not exist, then f1(t) will call f1(T&&), T is deduced as int&, type int& is lvalue, t is lvalue


    f0(std::forward<int>(t));   // T is deduced as int for f(T&&), type int is not rvalue, nor lvalue, t is rvalue
    f0(std::move(t));           // T is deduced as int for f(T&&), type int is not rvalue, nor lvalue, t is rvalue
    f0(t);                      // T is deduced as int for f(T&),  type int is not rvalue, nor lvalue, t is lvalue
                                //if f0(T&) not exist, then f0(t) will call f0(T&&), T is deduced as int&, type int& is lvalue, t is lvalue
}




void test_rvalue()
{

    f2(5);
    std::cout << std::boolalpha;
    std::cout << std::is_lvalue_reference<A>::value << '\n';        // A is not lvalue
    std::cout << std::is_rvalue_reference<A>::value << '\n';        // A is not rvalue
    std::cout << std::is_lvalue_reference<A&>::value << '\n';       // A& is lvalue
    std::cout << std::is_rvalue_reference<A&&>::value << '\n';      // A&& is rvalue
    std::cout << std::is_lvalue_reference<int>::value << '\n';      // int is not lvalue
    std::cout << std::is_rvalue_reference<int>::value << '\n';      // int is not rvalue
    std::cout << std::is_lvalue_reference<int&>::value << '\n';     // int& is lvalue
    std::cout << std::is_rvalue_reference<int&&>::value << '\n';    // int&& is rvalue

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM