为什么使用 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.
以下代码无法编译,因为 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.