我对以下完美转发函数感到困惑,其中模板参数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>
}
我不明白为什么T
中foo
的模板参数推断在这两种情况下是如此不同?什么是t
在函数foo
中的类型的根本区别和重要性。
std::move(a)
返回右值引用,b
已经是右值引用(但有一个名称)。
这是正确的,b
的类型是std::vector<int>
的右值引用,但就我的理解而言,它有一个名称,因此被认为是函数main中的左值?
任何人都可以为此发光: - )
答案 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&
}
此外,参考折叠规则是一个很好的阅读。
检查一下这是一个非常好的解释:
答案 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)
表达式 a
和b
都是左值,表达式std::move(a)
是右值。
参数T
的推论使用了特殊的引用折叠规则,因此t
的类型可以是左值或右值引用,以便绑定到函数调用参数。