我想使用该类的构造函数将一个大容器从返回值移动到另一个类中。如何制定参数以确保它不会被复制?
/* for the sake of simplicity, imagine this typedef to be global */
typedef std::unordered_map<std::string, unsigned int> umap;
umap foo()
{
umap m; /* fill with lots of data */
return m;
}
class Bar
{
public:
Bar(umap m) : bm(m) { }
private:
umap bm;
};
Bar myBar(foo()); // run foo and pass return value directly to Bar constructor
以上公式是否会触发相应的行为,或者我是否需要将构造函数的参数指定为rvalue-references,即容器为自己的移动语义做的方式?
public:
Bar(umap&& m) : bm(m) { }
或
public:
Bar(umap&& m) : bm(std::move(m)) { }
...
答案 0 :(得分:12)
如果您想支持移动和复制,最简单的方法是to pass by value:
Bar(umap m) : bm(std::move(m)) { }
现在你可以从左值和右值构建Bar
:
umap m;
Bar b1(m); // copies
Bar b2(std::move(m)); // moves
Bar b3(make_umap()); // moves
如果仅想要支持rvalues,那么请使用显式右值引用:
Bar(umap && m) : bm(std::move(m)) { }
始终需要std::move
,因为(m)
始终是左值。
答案 1 :(得分:4)
强制将rvalue传递给构造函数,并确保移动地图,如下所示:
Bar(umap &&m) : bm(std::move(m)) {}
让我们从Bar(umap m) : bm(m) {}
的第一个例子开始。这将执行至少1个副本,可能是2个。
bm(m)
将始终复制。 m
这里是左值,即使它绑定到右值。要使其移动,您必须将其包裹在std::move
中以将其重新转换为 rvalue 。我们得到bm(std::move(m))
。
Bar(umap m)
可能会复制。如果该值为 rvalue ,则会移入该值,但如果该值为左值,则会复制该值。由于我们要阻止所有副本,因此我们只需要让 rvalue 绑定。我们得到Bar(umap && m)
。
答案 2 :(得分:1)
除上述答案外:
我在你的例子中看到的是“复制省略”优化的经典案例。
What are copy elision and return value optimization?
实际的编译器不需要您的支持来优化事物,在这种特殊的返回值优化的情况下,没有必要处理rvalue引用。
答案 3 :(得分:0)
在C ++ 11中,您使用std::move
。如果您需要C ++ 03兼容性,可以通过bm
default-construct :bm()
,然后使用using std::swap; swap(bm, m);
。