11

在和同事解释对象的移动操作时,我基本上说移动操作不应该在容器中抛出异常,因为如果移动操作失败,那么就没有办法可靠地恢复原始对象。再想一想,我想知道这是否不正确,如果移动操作确实抛出,它可以将原始对象恢复到原始状态。

这样做的原因是,如果一个对象可以抛出,那么它不会因为将包含的对象从旧地址复制或移动到新地址而抛出,而是在资源获取失败时抛出。所以所有原始信息应该仍然存在。如果是这种情况,那么编译器是否应该无法逆转它为重构原始对象所做的操作?

一个操作可能是一种方式,比如移动一个整数,但在这种情况下它可能只是终止应用程序,也许如果开发人员想避免这种方式操作可以使用交换方法来代替。

这只能在默认的移动运算符上实现,就像有任何额外的逻辑一样,编译器可能很难进行反向部分转换。

我是否过于简单化了事情?有没有我错过的东西可以让容器在没有非抛出移动构造函数/操作符的情况下移动对象?

4

2 回答 2

10

您可以在容器中使用带有投掷动作的类型,例如vector可以移动其元素的容器。但是,此类容器不会使用投掷移动操作。

假设您有vector10 个投掷移动元素。以及调整自身大小的vector需要。因此它将 5 个对象移动到新内存中,但第 6 个对象会抛出。好吧,没关系;构造失败,因此假设第 6 个对象的值很好。也就是说,无论该类型的异常保证是什么,都将是如何工作的。

但是随后,由于一个对象的移动失败,vector需要将最后 5 个对象移回第一个数组,因为vector它试图提供一个强大的异常保证。这是一个问题,因为后退本身可能会失败

当修复故障的过程本身失败时,C++ 通常没有有效的答案。您可以在异常中看到这一点;由于异常失败,您不能从在展开过程中调用的析构函数发出异常。std::terminate在这种情况下发生。

也是如此vector。如果后退失败,vector没有理智的答案。因此,如果vector不能保证恢复其先前的数组状态是noexcept,那么它将使用复制,因为这可以提供这种保证。

于 2018-05-29T17:56:08.030 回答
0

首先,我很难想象在移动操作中获取资源的对象。想想看 -unique_ptr只是传递指针而不获取任何东西,对于shared_ptr. string, , vector, 所有容器等只是窃取指向先前在默认或复制构造函数中获取的资源的指针。我觉得从移动构造函数中抛出就像从析构函数中抛出一样。当然,继续,在你的膝盖上开枪。但是好的,我可以接受存在例外。

所以让我们移到第二点 - 当移动时,可能有一段时间实际上两个对象(移出和移至)都无效。并且从这种情况下回滚将需要调用额外的“魔术”函数来修复其中一个。所以看起来数据无法修复,因为标准没有定义这样的功能。

于 2018-05-29T18:00:39.277 回答