27

在即将到来的 C++0x 标准中,当在移动构造函数内/期间抛出异常时会发生什么?

原始对象会保留吗?还是原始对象和移动对象都处于未定义状态?语言提供了哪些保证?

4

3 回答 3

11

I believe that the standards committee originally attempted to make it so move constructors would not be allowed to throw exceptions, but (at least as of today) found that trying to enforce that had too many pitfalls.

Proposal N3050, "Allowing Move Constructors to Throw (Rev 1)", has been incorporated into the draft standard. Essentially the proposal adds the capability for move constructors to throw, but to disallow 'throwing' moves to be used for certain operations where strong exception safety guarantees were needed (the library will fall back to copying the object if a non-throwing move isn't available).

If you mark a move constructor as non-throwing (noexcept) and an exception is thrown, std::terminate() will be called.

It might also be worth reading a blog article by David Abrahams that discusses the issues that N3050 was intended to address:

于 2011-01-19T06:44:14.280 回答
3

取决于要移动的类型。当然,可以从移动 ctor 显式抛出异常,但也可以从移动 ctor 隐式调用子对象的复制 ctor。该复制ctor可能会做一些可能会抛出的事情,例如分配内存。因此,对于源对象,最低保证是原始值可能保留也可能不保留,但它仍应处于可破坏状态。

对于被移动到的对象,它与当前 C++ 中的 ctor 抛出相同:销毁任何构造的基和成员,执行 ctor 的函数 try 处理程序(如果有),然后传播异常。详细信息在 N3225 §15.2p2 中。

特别要注意,容器要求它们的分配器类型没有投掷移动 ctor:

分配器的这种移动构造不应通过异常退出。[N3225 §23.2p8]

这允许容器移动分配器,并在移动或复制项目时出现异常时使用这些分配器清理它们的项目。

于 2011-01-19T06:12:47.840 回答
2

您的问题相当于一个关于例外保证的问题。有 3 种类型的例外保证(适用于函数):

  • 根本没有例外保证(不是真正的类型......但如果不关心此事,它可能会发生)
  • 基本异常保证:技术上正确,但功能上不正确(即没有资源泄漏,程序将在没有突然停止的情况下终止,但它可能具有不希望的副作用,例如兑现付款但未注册命令)
  • 强异常保证:全有或全无(如事务),即要么一切都正确完成,要么我们回滚到之前的状态。
  • 无抛出异常保证:这永远不会抛出,所以不用担心。

当你编写你的函数时,你通常会选择带有自己保证的现有函数。很难增加异常保证,即您通常受到使用的最弱保证的限制。

写你的问题,如果抛出异常,原始对象至少需要一个强异常保证

那么,如果在 move-construction 期间抛出异常会发生什么?这取决于子对象表现出的保证以及您组合调用的方式......

  1. 如果从构造函数中抛出异常,则不会构建对象,并且所有构建的子对象都会以相反的顺序被销毁。此规则也适用于移动构造函数
  2. 除非您将构造函数“包装”在 try catch 中并以某种方式恢复已移动的对象,否则它们将丢失资源。请注意,无论如何它们必须仍处于可破坏状态,因此从技术上讲,该程序是正确的。

就异常保证而言,这意味着默认情况下,如果所有子对象的构造函数至少满足Basic Exception Guarantee,那么您的移动构造函数也将满足,无需特别注意。

但是,即使所有子对象的构造函数都满足强异常保证,您也不太可能成功地让自己的移动构造函数满足它:这与链接事务不会产生事务的问题相同。

如果只有一个子对象的构造函数可以抛出,并且它满足强异常保证,那么如果你先初始化抛出对象,你的移动构造函数自然会遇到它。

希望这会有所帮助......例外是驯服的野兽:)

于 2011-01-19T08:25:39.987 回答