Nicolai M. Josuttis 在他的《C++ Move Semantics》一书中指出,使用生成的移动构造函数在可移动成员中移动包含不可移动成员的对象会移动除不可移动成员(被复制)之外的所有成员。下面的代码片段是书中示例的变体。
#include <iostream>
class Copyable {
std::string name;
public:
explicit Copyable(std::string name): name(std::move(name)) {}
// Copying enabled
Copyable(const Copyable& other): name(other.name) {
std::cout << "Copyable copy ctor" << std::endl;
}
Copyable& operator=(const Copyable& other) {
name=other.name;
std::cout << "Copyable op=" << std::endl;
return *this;
}
// Moving disabled with no copy fallback
Copyable(Copyable&&) = delete;
Copyable& operator=(Copyable&&) = delete;
};
class Movable {
std::string name;
public:
explicit Movable(std::string name): name(std::move(name)) {}
// Copying enabled
Movable(const Movable& other): name(other.name) {
std::cout << "Movable copy ctor" << std::endl;
}
Movable& operator=(const Movable& other) {
name=other.name;
std::cout << "Movable op=" << std::endl;
return *this;
}
// Moving enabled
Movable(Movable&& other) noexcept: name(std::move(other.name)) {
std::cout << "Movable move ctor" << std::endl;
}
Movable& operator=(Movable&& other) noexcept {
name = std::move(other.name);
std::cout << "Movable move op=" << std::endl;
return *this;
}
};
class Container {
Copyable copyable;
Movable movable;
public:
Container(Copyable copyable, Movable movable): copyable(copyable), movable(std::move(movable)) {}
// Both copying and moving enabled by default
};
int main() {
Copyable c{"copyable"};
Movable m{"movable"};
Container container{c, m};
Container container2{std::move(container)};
}
Run Code Online (Sandbox Code Playgroud)
在 x86-64 上使用 GCC 使用 C++17 标准进行编译,会产生以下输出:
容器创建并初始化:
Copyable copy ctor
Movable copy ctor
Copyable copy ctor
Movable move ctor
Run Code Online (Sandbox Code Playgroud)
集装箱移动:
Copyable copy ctor
Movable copy ctor
Run Code Online (Sandbox Code Playgroud)
一旦容器移动,就不需要为可移动部件调用移动函数。根据这本书, move ctor 应该被称为 Movable 成员,不是吗?
我不确定这本书的作者的意思,但这里引用了cppreference:
如果满足以下任一条件,则类 T 的隐式声明或默认移动构造函数被定义为已删除:
- T 具有无法移动的非静态数据成员(具有已删除、不可访问或不明确的移动构造函数);
- [...]
因此 的移动构造函数Container被隐式声明(也称为“生成”)为已删除。你不能使用它。相反,std::move(container)绑定到复制构造函数的 const 引用,这就是所谓的。
请注意,如果您尝试像这样将移动构造函数声明为默认构造函数,编译器应该给您一条错误消息:
Container(Container&& other) noexcept = default;
Run Code Online (Sandbox Code Playgroud)
例如,海湾合作委员会说:
错误:使用已删除的函数 'Container::Container(Container&&)'
注意:'Container::Container(Container&&)' 被隐式删除,因为默认定义格式不正确
你可以获得书中描述的行为,但你必须自己编写。像这样的东西:
Container(Container&& other) :
copyable(other.copyable),
movable(std::move(other.movable)) {
}
Run Code Online (Sandbox Code Playgroud)
...虽然我不知道你为什么想要这样做。在具体场景中,不能动一定是有充分理由的Copyable。公平地说,可复制但不可移动的类型似乎不是很有用。尽管如此,我并不期望书中描述的行为是默认(隐式)行为。