4

我想当通用引用参数与右值引用参数匹配时,会返回一个右值引用参数。但是,我的测试表明,右值引用被通用引用函数模板变成了左值引用。为什么会这样?

#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;
}

在 GCC 和 VC++2010 中,结果如下:

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 的参数tf2一个右值引用,但是当传递给 时f1,参数变成了一个左值引用。它不应该保留右值f1吗?

4

4 回答 4

8

原因是命名的右值引用被视为左值。

将t传递给f1以保留右值性时,您应该在f2中使用std::move :

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

在这里你可以找到一个很好的解释。

于 2016-12-28T11:18:08.120 回答
3

调用f1(t)时,参数是表达式t。不是static_cast<decltype(t)>(t)什么的。你的检查decltype(t)与调用无关f1(t)

该表达式t具有类型int和值类别lvalue。(一个经验法则是,如果你可以获取一个表达式的地址,那么它就是一个左值,你当然可以写&t)。引用变量最初被声明为引用的“信息”仅通过decltype检查可见。

由于f1用左值调用,T因此推导出int&.

注意。如果您想在. f1_ 对于右值参数,推导为非引用类型,例如,如果您通过使其 do then 's is和in is 来修复它。decltype(t)Tis_rvalue_referencef1Tf2f1(std::move(t));f1Tintdecltype(t)f1int&&

于 2016-12-28T13:14:43.227 回答
0

在学习了 C++11 标准之后,我对我f1(t);f2. 我在这里描述它,看看我是否正确:

  1. f2,t是类型的左值int&&(不是int,这是一个重要的区别)
  2. 调用f1(t);导致类型被推断如下:

    2.1 当Tinf1被赋予一个左值时,它被推断为该左值类型的引用或int&& &

    2.2 参考坍塌原因int&& &成为int &。这是 的值T

  3. 由于参数 off1被声明为T&&,所以参数tin的类型f1int & &&。所以引用折叠第二次发生以推断tas的类型int &

  4. 因此 T=int &并且参数的类型tint &。即参数t是类型的左值int &

任何意见?

于 2016-12-28T14:45:38.740 回答
-1

使用 std::forward 将 t 保持为右值。

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

t 参数作为右值传递并推导出为 int 类型。(看这里是怎么扣的)

std::forward(t) 返回一个 int&& 类型的右值,以便调用 f1(int&&)

没有 std::forward(t),f1(t) 接受 int 类型的参数并调用 f1(int&)

更新:请注意之间的区别

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

在模板中,T 是根据函数参数推导出来的,而 t 的值类别始终是 f2 中的左值,并且

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

总是右值。

#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

}
于 2021-01-17T12:40:33.987 回答