我应该如何编写函数参数来强制执行移动而不是复制?

时间:2014-01-08 19:31:01

标签: c++ c++11 parameters move-semantics

我想使用该类的构造函数将一个大容器从返回值移动到另一个类中。如何制定参数以确保它不会被复制?

/* 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)) { }

...

4 个答案:

答案 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);