为什么使用 std::vector::push_back 的移动变体会调用移动项的复制构造函数?

Why does using the move variant of std::vector::push_back invoke the moved item's copy constructor?

以下代码无法编译,因为 push_back 正在尝试调用 MoveOnlyClass 中的复制构造函数,该类已被删除:

class MoveOnlyClass
{
public:
    MoveOnlyClass() {};
    MoveOnlyClass& operator=(const MoveOnlyClass& other) = delete;
    MoveOnlyClass(const MoveOnlyClass& other) = delete;
};

int main()
{
    std::vector<MoveOnlyClass> vec;
    vec.push_back(std::move(MoveOnlyClass()));
}

为什么会这样?当然,vector 唯一应该调用的是移动构造函数。将对象移动到向量中的正确方法是什么?

删除复制constructor/copy赋值函数也隐式删除了move-constructor/move赋值函数。如果您打算使对象可移动但不可复制,您还需要 default 移动构造函数。

class MoveOnlyClass
{
public:
    MoveOnlyClass() {};
    MoveOnlyClass& operator=(const MoveOnlyClass& other) = delete;
    MoveOnlyClass(const MoveOnlyClass& other) = delete;
    MoveOnlyClass& operator=(MoveOnlyClass&& other) = default;
    MoveOnlyClass(MoveOnlyClass&& other) = default;
};

//Will now compile as you expect
int main()
{
    std::vector<MoveOnlyClass> vec;
    vec.push_back(std::move(MoveOnlyClass()));
}

此外,std::move(T())是多余的;像这样就地构建对象已经使它成为 R 值,并且在不需要时使用 std::move 可能会阻止某些类型的编译器优化(例如 Copy Ellision)。

@Xirema 的回答解决了代码中的问题,并解释了为什么会这样。

我只想用语言规范中的适当摘录来支持它,使事情正式化。所以从 [class.copy.ctor¶8]:

(8) If the definition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if

  • (8.1) X does not have a user-declared copy constructor,

为此,我们应该添加声明为已删除仍在声明中。因此,根据这一点,在您的情况下,我们不会得到 隐式声明的移动构造函数 ,因为我们已经有了一个 用户声明的复制构造函数

此外,在[dcl.fct.def.delete¶3]下:

One can make a class uncopyable, i.e., move-only, by using deleted definitions of the copy constructor and copy assignment operator, and then providing defaulted definitions of the move constructor and move assignment operator.