带有右值引用的C++:std::move不是在移动内容
C++: std::move with rvalue reference is not moving contents
示例程序:
#include <iostream>
#include <string>
#include <vector>
template <typename T>
void print(const T& _vec)
{
for( auto c: _vec )
std::cout << c << ",";
}
typedef std::vector<std::string> vecstr_t;
struct Trade
{
explicit Trade(vecstr_t&& vec) : _vec(vec )
{
}
vecstr_t _vec;
};
int main()
{
vecstr_t tmpV = {"ONE", "TWO", "THREE", "FOUR"};
std::cout << "size 1:" << tmpV.size() << "t"; print(tmpV); std::cout << "n" ;
Trade t(std::move(tmpV));
std::cout << "size 2:" << tmpV.size() << "t"; print(tmpV); std::cout << "n" ; // expted tmpV should be e,pty but it has original contents
print(t._vec);
}
我预计尺寸2:应该为零,但输出为:
size 1:4 ONE,TWO,THREE,FOUR,
size 2:4 ONE,TWO,THREE,FOUR,
ONE,TWO,THREE,FOUR,
explicit Trade(vecstr_t&& vec) : _vec(vec)
{}
在上面的构造函数中,即使vec
是对vecstr_t
的类型右值引用,它本身也是一个左值。要记住的基本规则是——如果它有一个名称,它就是一个左值。
很少有上下文可以自动移动左值(例如按值返回对象的函数的return语句(,但构造函数的mem初始值设定项列表不是其中之一。
在您的示例中,_vec
是从vec
复制构建的。如果您希望它是移动构造的,请使用std::move
。
explicit Trade(vecstr_t&& vec) : _vec(std::move(vec))
{}
现在,对print
的第二次调用将不会打印任何内容。请注意,从技术上讲,第二个调用可以打印非零大小,因为从vector
移动的内容是未指定的。但在大多数(可能是所有(实现中,您将看到一个空的vector
。
现场演示
你在下面的评论说,你的意图是同时接受右值和左值,只在前者的情况下移动,否则复制论点。正如目前所写的,你的构造函数只接受右值,而不接受左值。有几种不同的选择可以实现你想要的。
最简单的方法可能是更改参数,使其按值接受参数,然后无条件地移动。
explicit Trade(vecstr_t vec) : _vec(std::move(vec))
{}
这种方法的缺点是,您可能需要额外的vector
移动构造,但移动构造vector
很便宜,在大多数情况下应该使用此选项。
第二个选项是创建构造函数的两个重载
explicit Trade(vecstr_t&& vec) : _vec(std::move(vec)) {}
explicit Trade(vecstr_t const& vec) : _vec(vec) {}
这种方法的缺点是,重载的数量将随着构造函数参数数量的增加而呈指数级增加。
第三种选择是使用完美的转发。
template<typename V>
explicit Trade(V&& vec) : _vec(std::forward<V>(vec)) {}
当构造函数将参数转发到构造_vec
时,上面的代码将保留传递给构造函数的参数的值类别。这意味着,如果vec
是右值,则将调用vecstr_t
移动构造函数。如果它是一个左值,它将从中复制。
此解决方案的缺点是,构造函数将接受任何类型的参数,而不仅仅是vecstr_t
,如果该参数不能转换为vecstr_t
,则mem初始值设定项列表中的move/copy构造将失败。这可能会导致用户感到困惑的错误消息。
- 当 std::move 与 C 样式数组或不移动对象时会发生什么
- 如何在没有 std::move 的情况下移动临时对象
- 为什么 std::move 不将默认移动构造函数中的源变量更改为默认值?
- 使用 std::move 将参数传递给函数,如果该参数声明为按值传递或使用移动操作数 &&,是否有区别?
- C++:我应该在 return 语句中显式使用 std::move() 来强制移动吗?
- 如何使用 std::move() 将 char[] 移动到向量<char>
- 谁负责释放由 std::move 移动的资源?
- 当使用 std::move 和 lambda 时,何时会发生移动
- 移动变量是否有效,可以在 std::move 之后使用
- C 可以使用MOVE SEMATICS将数据从一个向量移动到另一个向量
- 为什么 std::optional 不允许"move construct and copy assign only"类型的移动分配?
- std::move在将std::string移动到另一个线程时出错
- 为什么 list unique_ptr 的 std::move() 没有<unique_ptr>真正移动它?
- 了解移动构造函数、std::move 和析构函数
- 如果在没有move构造函数的情况下移动对象,会发生什么
- 如果我们只定义复制构造函数/oper=,为什么移动构造函数/move赋值没有隐式声明和定义为删除
- 移动构造函数和 std::move 混淆
- 在移动运算符/赋值运算符中,我应该使用std::move还是std::forward
- 将类(没有默认构造函数)移动到另一个类的move构造函数中
- Do move构造函数需要可移动的属性