c++ - 了解完美转发
问题描述
据我了解,作为参数传递给函数的右值变为左值,如果参数作为右值传递,std::forward 返回右值,如果作为左值传递,则返回左值。这是我的课:
#include <string>
#include <iostream>
struct MyClass
{
MyClass()
{
std::cout << "default";
}
MyClass(const MyClass& copy)
{
std::cout << "copy";
}
MyClass& operator= (const MyClass& right)
{
std::cout << "=";
return *this;
}
MyClass& operator= (const MyClass&& right)
{
std::cout << "mov =";
return *this;
}
MyClass(MyClass&& mov)
{
std::cout << "mov constructor";
}
};
void foo(MyClass s)
{
MyClass z = MyClass(std::forward<MyClass>(s));
}
void main()
{
auto a = MyClass();
foo(MyClass()); //z is created by move_constructor
foo(a); //z is created by move_constructor, but I think it must be created using copy constructor
}
我的问题是:为什么在这两种情况下都使用 move_constructor 创建 z 变量。我认为它必须在第一种情况下移动 foo(MyClass()) 并在第二种情况下复制 foo(a)。在第二种情况下,我将左值作为参数 s 传递,并且 std::forward 必须返回左值,然后将其作为左值引用传递给 MyClass 构造函数。我哪里错了?
解决方案
我认为你已经足够困惑了。只有当通用引用发挥作用时,前向的作用才重要,而通用引用类似于T&& t
但仅当 T 是模板参数时。
例如,invoid foo(X&& x);
x
不是转发引用,它是普通的右值引用,转发它没有任何意义。相反,std::move
如果要保留它的右值性,则使用它,否则它会变成左值:
void foo(X&& x) {
bar(x); // calls bar with an l-value x, x should be not moved from
baz(std::move(x)); // calls bar with an r-value x, x is likely moved from after this and probably unusable
}
换句话说,上面的函数foo
是专门设计为将右值引用作为它的参数,并且不会接受其他任何东西。作为函数编写者,您以这种方式定义了它的合同。
相反,在上下文中 liketemplate <class T> void foo(T&& t)
t
是转发引用。由于引用折叠规则,它可以是右值或左值引用,具体取决于在调用站点给函数 foo 的表达式的值。在这种情况下,您使用
template<class T>
void foo(T&& t) {
// bar is called with value matching the one at the call site
bar(std::forward<T>(t));
}
推荐阅读
- python - 未找到 Docker build requirements.txt (LINUX WSL)
- ubuntu - 如何将 1920x1200 分辨率添加到 Ubuntu 20.04
- android - 无法转换 constraintlayout-solver-2.0.4.jar
- yii2 - Yii2 Stripe Webhook 测试:“[错误] 发布失败”
- javascript - 传单 - 功能仅适用于缩放
- mongodb - 错误的 pem 文件或 Docker 桌面权限
- multidimensional-array - 获取 java.lang.ArrayIndexOutOfBoundsException:将 2d(动态列)数组转换为 1d 数组时,索引 3 超出长度 3 的范围
- eclipse - Lombok 安装程序不适用于带有 Gradle 项目的 Eclipse
- python - 为什么 list[0:len(list)+1:-1] 返回一个空列表?
- nginx - NGINX 不会在自定义目录中提供 html