c++ - 隐式移动与复制操作和遏制
问题描述
当一个类有一个未定义其移动操作的成员时,我很难理解隐式移动操作:
int main() {
struct A // no move: move = copy
{
A() = default;
A(const A&) {
cout << "A'copy-ctor\n";
};
A& operator=(const A&) {
cout << "A'copy-assign\n";
return *this;
}
};
struct B
{
B() = default;
A a; // does this make B non-moveable?
unique_ptr<int> upi;
// B(B&&) noexcept = default;
// B& operator=(B&&)noexcept = default;
};
A a;
A a2 = std::move(a); // ok use copy ctor instead of move one
a2 = std::move(a); // ok use copy assignment instead of move one
B b;
B b2 = std::move(b); // why this works?
b = std::move(b2); // and this works?
// b = b2; // error: copy deleted because of non-copyable member upi
cout << "\nDone!\n";
}
所以我看到的A
是一个不可移动的类,因为它定义了复制控制操作,所以它只能被复制,并且任何试图移动这个类的对象,都会使用相应的复制操作。
如果我是正确的,直到这里它是好的。但是B
有一个不可复制的对象upi
,unique_ptr
因此复制操作被定义为已删除的函数,因此我们无法复制此类的对象。但是这个类有一个不可移动的对象a
,因此我认为这个类 ( B
) 既不能复制也不能移动。但是为什么初始化b2
和分配b
工作正常?究竟会发生什么?
B b2 = std::move(b); // ok?!
为什么上面的行调用了类的复制构造函数A
并且它调用了移动构造函数B
?
- 对我来说情况变得更糟:如果我取消注释中的移动操作行,
B
上面的初始化将不会编译抱怨引用已删除的函数,赋值也是如此!
谁能帮我到底发生了什么?在在这里发布问题之前,我已经在 cppreference 和许多网站上搜索并阅读过。
输出:
A'copy-ctor
A'copy-assign
A'copy-ctor
A'copy-assign
Done!
解决方案
请记住在 C++ 中“移动”数据的含义(假设我们遵循通常的约定)。如果您将 object 移动x
到 object y
,则y
接收所有之前的数据x
并且x
是……好吧,我们不在乎是什么x
,只要它仍然对销毁有效。通常我们认为x
丢失了所有数据,但这不是必需的。所需要的x
就是有效的。如果x
最终得到与 相同的数据y
,我们不在乎。
复制x
到y
导致y
接收 中的所有数据x
,并x
保持有效状态(假设复制操作遵循约定并且没有错误)。因此,复制算作移动。除了复制操作之外还定义移动操作的原因不是为了允许新的东西,而是为了在某些情况下允许更高的效率。除非您采取措施防止移动,否则可以移动任何可以复制的东西。
所以我看到的
A
是一个不可移动的类,因为它定义了复制控制操作,所以它只能被复制,任何试图移动这个类的对象,都会使用相应的复制操作。
我看到的A
是一个可移动的类(尽管缺少移动构造函数和移动赋值),因为它定义了它的复制控制操作。任何移动此类对象的尝试都将依赖于相应的复制操作。如果您希望类可复制但不可移动,则需要删除移动操作,同时保留复制操作。(试试看。添加A(A&&) = delete;
到您的定义中A
。)
该类B
有一个可以移动或复制的成员,以及一个可以移动但不能复制的成员。所以B
它本身可以移动但不能复制。当B
被移动时,unique_ptr
成员将按照您的预期移动,并且A
成员将被复制(移动类型对象的后备A
)。
对我来说情况变得更糟:如果我取消注释中的移动操作行,
B
上面的初始化将不会编译抱怨引用已删除的函数,赋值也是如此!
更仔细地阅读错误消息。当我复制这个结果时,“使用已删除函数”错误后面跟着一条提供更多详细信息的注释:移动构造函数被删除,因为“它的异常规范与隐式异常规范不匹配”。删除noexcept
关键字允许代码编译(使用 gcc 9.2 和 6.1)。
或者,您可以添加noexcept
到复制构造函数和复制赋值A
(保持noexcept
的移动操作B
)。这是演示默认移动操作B
使用 的复制操作的一种方式A
。
推荐阅读
- android - 在 Junit 中模拟 DateFormat 并设置时区
- c# - C# 带参数触发 EventHandler
- r - R - 根据条件排除参与者(超过 10% 的响应——变量:延迟——小于 300 毫秒)
- javascript - 无法绑定到“ngModel”,因为它不是“输入”的已知属性,如何在角度 9 中修复?
- outlook -
(false) 更新“HTMLBody”时触发 - java - 尝试在 cmd 中运行可执行 Jar 时出错
- javascript - React - 如何刷新组件?
- javascript - Rest API 列表项目计数代码适用于 SP 2013 而不是 SPO
- node.js - 在 TypeScript 中将 Chai 自定义插件声明为 NodeJS 全局变量
- reactjs - javascript / react中点前的问号