为什么这需要一个明确的std :: move?

时间:2014-01-23 23:02:13

标签: c++ c++11

假设我有一个Foo类,其中包含std::vectorstd::unique_ptr来自另一个类Bar的{​​{1}}个对象。

typedef std::unique_ptr<Bar> UniqueBar;

class Foo {
    std::vector<UniqueBar> bars;
public:
    void AddBar(UniqueBar&& bar);
};

void Foo::AddBar(UniqueBar&& bar) {
    bars.push_back(bar);
}

这导致编译错误(在g ++ 4.8.1中)说明std::unique_ptr的复制构造函数被删除,这是合理的。这里的问题是,因为bar参数已经是一个右值引用,为什么调用std::unique_ptr的拷贝构造函数而不是它的移动构造函数?

如果我在std::move中明确调用Foo::AddBar,那么编译问题就会消失,但我不明白为什么需要这样做。我认为这是多余的。

那么,我错过了什么?

4 个答案:

答案 0 :(得分:16)

基本上,每个具有名称的对象都是左值。当您使用右值引用将对象传递给函数时,该函数实际上会看到左值:它被命名。然而,右值引用的作用是表明它来自一个准备传输的对象。

换句话说,右值参考是不对称的:

  • 他们只能接收rvalues,即临时对象,即将离开的对象,或看起来像是rvalues的对象(例如,std::move(o)的结果)
  • 然而,右值参考本身看起来像左值

答案 1 :(得分:9)

看起来很混乱, rvalue-reference 绑定到 rvalue ,但用作表达式是 lvalue

答案 2 :(得分:8)

bar实际上是左值,因此您需要将其传递给std::move,以便在调用push_back时将其视为右值。

Foo::AddBar(UniqueBar&& bar)重载只是确保在调用Foo::AddBar时传递右值时选择此重载。但bar参数本身有一个名称,并且是一个左值。

答案 3 :(得分:3)

bar被定义为rvalue-reference,但其 value-category 是左值。这是因为该对象具有名称。如果它有名字,那就是左值。因此,显式std::move是必要的,因为意图是删除名称并返回xvalue(eXpiring-rvalue)。