右值引用匹配(完全转发示例)

rvalue reference matching (perfect forwarding example)

本文关键字:转发 引用      更新时间:2023-10-16

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

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

我不明白为什么fooT的模板参数演绎在这两种情况下有如此大的不同?t的根本区别是什么?重要的是,foo的功能类型是什么?

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

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

有谁能解释一下吗:-)

当&&与模板一起使用。

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

"时,,出现在类型推断上下文中,T&&获得特殊的意思。当func被实例化时,T取决于参数是否传递给func的是左值或右值。如果它是U型的左值,T被演绎为U&如果是右值,则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&
}

同时,引用折叠规则也是一个很好的选择。

看看这个很好的解释:

完全转发

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

在另一种情况下,您将T推断为vector<int>&。所以参数的类型是vector<int> & &&…或者应该是,但是引用引用是不允许的。引用崩溃接管,任何涉及左值引用的双引用都变成左值引用。所以你传递的是左值引用

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

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

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

是的,正是这样。如果考虑一下右值引用函数的参数,这就更有意义了:调用者指定函数可以对它得到的对象做任何它想做的事情。所以在函数体内部,为了确保代码可以对它做任何想做的事情,参数应该被视为左值。同样的参数也可以用于其他右值引用,包括示例中的b,尽管范围较小。

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

形参T的演绎使用了特殊的引用折叠规则,因此t的类型要么是左值引用,要么是右值引用,以绑定到函数调用实参。