右值参考匹配(完美转发示例)

时间:2015-10-04 15:50:35

标签: c++ c++11 rvalue-reference

我对以下完美转发函数感到困惑,其中模板参数T可以匹配rvalue或左值引用:

template<typename T>
void foo(T&& t){
    T::A; // intended error to inspect type
}

int main(){
    std::vector<int> a;
    std::vector<int> && b = std::move(a);

    foo(b);             // T is std::vector<int> & 
    foo(std::move(a));  // T is std::vector<int>
}

我不明白为什么Tfoo的模板参数推断在这两种情况下是如此不同?什么是t在函数foo中的类型的根本区别和重要性。

std::move(a)返回右值引用,b已经是右值引用(但有一个名称)。

这是正确的,b的类型是std::vector<int>的右值引用,但就我的理解而言,它有一个名称,因此被认为是函数main中的左值?

任何人都可以为此发光: - )

4 个答案:

答案 0 :(得分:2)

当&amp;&amp;和与模板一起使用。

template <class T>
void func(T&& t) {
}
  

“当&amp;&amp;&amp;&amp;&amp;&amp;&amp;&amp;&amp;&amp;&amp;&amp;&amp;&amp;&amp;&amp;   含义。当func被实例化时,T取决于参数是否   传递给func是左值或左值。如果它是U型的左值,   T推断为U&amp ;.如果它是一个右值,则T推导到U:“

func(4);            // 4 is an rvalue: T deduced to int

double d = 3.14;
func(d);            // d is an lvalue; T deduced to double&

float f() {...}
func(f());          // f() is an rvalue; T deduced to float

int bar(int i) {
  func(i);          // i is an lvalue; T deduced to int&
}

此外,参考折叠规则是一个很好的阅读。

检查一下这是一个非常好的解释:

perfect forwarding

答案 1 :(得分:1)

如果考虑函数的签名,参数的类型为T&&。在第二个示例中,T推导为vector<int>,这意味着函数的参数类型为vector<int>&&。所以你仍然通过(右值)参考。

在另一种情况下,您将T推断为vector<int>&。所以参数的类型是vector<int> & && ......或者它会是,但是不允许引用引用。参考折叠接管,任何涉及左值参考的双引用都将成为左值参考。所以你要通过左值参考。

就b而言,这是一个众所周知的rvalue引用问题。本质上,b的类型是右值引用,但b本身仍然具有左值的值类别。可以这样想:b本身是一个变量,它必须存在于某个堆栈中,并且有一个地址。所以它是一个左值。这正是在需要转发参数时调用std :: forward的方法。如果您没有这样做,那么它们将始终作为左值参数转发。

我真的推荐这篇Scott Meyers的文章:https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers。仔细阅读!

答案 2 :(得分:1)

  

这是正确的,b的类型是std::vector<int>的右值引用,但就我的理解而言,它有一个名称,因此被认为是函数main中的左值?

是的,就是这样。如果你考虑rvalue引用函数参数会更有意义:调用者指定函数可以对它获得的对象做任何想做的事情。所以从函数体内部来看,为了确保代码真的可以随意做任何事情,该参数应该被视为左值。同样的参数也可以用于其他右值引用,包括示例中的b,尽管程度较小。

答案 3 :(得分:0)

表达式 ab都是左值,表达式std::move(a)是右值。

参数T的推论使用了特殊的引用折叠规则,因此t的类型可以是左值或右值引用,以便绑定到函数调用参数。