右值引用匹配(完美转发示例)
rvalue reference matching (perfect forwarding example)
我对下面的完美转发函数感到困惑,其中模板参数 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>
}
我不明白为什么 T
和 foo
的模板参数推导在这两种情况下如此不同? t
在函数 foo
.
中的基本区别和重要之处是什么
std::move(a)
returns 一个右值引用并且 b
已经是一个右值引用(但有一个名称)。
是这样吗,b
s 类型是对 std::vector<int>
的右值引用,但据我所知,它有一个名称,因此被认为是函数 main 中的左值?
谁能对此有所启发:-)
&& 与模板一起使用时有一个特殊的类型推导规则。
template <class T>
void func(T&& t) {
}
"When && appears in a type-deducing context, T&& acquires a special
meaning. When func is instantiated, T depends on whether the argument
passed to func is an lvalue or an rvalue. If it's an lvalue of type U,
T is deduced to U&. If it's an rvalue, T is deduced to 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。仔细阅读!
Is that right that, b
s type is a rvalue reference to std::vector<int>
, but as far as my understanding goes, it has a name and is thus considered an lvalue in function main?
是的,就是这样。如果您考虑右值引用函数参数,则更有意义:调用者指定该函数可以对其获取的对象做任何它想做的事情。所以在函数体内部,为了确保代码真的可以为所欲为,参数应该被视为左值。同样的论点也可以用于其他右值引用,包括您示例中的 b
,尽管程度较小。
表达式 a
和b
都是左值,表达式std::move(a)
是右值。
参数 T
的推导使用了特殊的引用折叠规则,因此 t
的类型是左值或右值引用,根据需要绑定到函数调用参数。
我对下面的完美转发函数感到困惑,其中模板参数 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>
}
我不明白为什么 T
和 foo
的模板参数推导在这两种情况下如此不同? t
在函数 foo
.
std::move(a)
returns 一个右值引用并且 b
已经是一个右值引用(但有一个名称)。
是这样吗,b
s 类型是对 std::vector<int>
的右值引用,但据我所知,它有一个名称,因此被认为是函数 main 中的左值?
谁能对此有所启发:-)
&& 与模板一起使用时有一个特殊的类型推导规则。
template <class T>
void func(T&& t) {
}
"When && appears in a type-deducing context, T&& acquires a special meaning. When func is instantiated, T depends on whether the argument passed to func is an lvalue or an rvalue. If it's an lvalue of type U, T is deduced to U&. If it's an rvalue, T is deduced to 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。仔细阅读!
Is that right that,
b
s type is a rvalue reference tostd::vector<int>
, but as far as my understanding goes, it has a name and is thus considered an lvalue in function main?
是的,就是这样。如果您考虑右值引用函数参数,则更有意义:调用者指定该函数可以对其获取的对象做任何它想做的事情。所以在函数体内部,为了确保代码真的可以为所欲为,参数应该被视为左值。同样的论点也可以用于其他右值引用,包括您示例中的 b
,尽管程度较小。
表达式 a
和b
都是左值,表达式std::move(a)
是右值。
参数 T
的推导使用了特殊的引用折叠规则,因此 t
的类型是左值或右值引用,根据需要绑定到函数调用参数。