在即将到来的 C++0x 标准中,当在移动构造函数内/期间抛出异常时会发生什么?
原始对象会保留吗?还是原始对象和移动对象都处于未定义状态?语言提供了哪些保证?
在即将到来的 C++0x 标准中,当在移动构造函数内/期间抛出异常时会发生什么?
原始对象会保留吗?还是原始对象和移动对象都处于未定义状态?语言提供了哪些保证?
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:
取决于要移动的类型。当然,可以从移动 ctor 显式抛出异常,但也可以从移动 ctor 隐式调用子对象的复制 ctor。该复制ctor可能会做一些可能会抛出的事情,例如分配内存。因此,对于源对象,最低保证是原始值可能保留也可能不保留,但它仍应处于可破坏状态。
对于被移动到的对象,它与当前 C++ 中的 ctor 抛出相同:销毁任何构造的基和成员,执行 ctor 的函数 try 处理程序(如果有),然后传播异常。详细信息在 N3225 §15.2p2 中。
特别要注意,容器要求它们的分配器类型没有投掷移动 ctor:
分配器的这种移动构造不应通过异常退出。[N3225 §23.2p8]
这允许容器移动分配器,并在移动或复制项目时出现异常时使用这些分配器清理它们的项目。
您的问题相当于一个关于例外保证的问题。有 3 种类型的例外保证(适用于函数):
当你编写你的函数时,你通常会选择带有自己保证的现有函数。很难增加异常保证,即您通常受到使用的最弱保证的限制。
写你的问题,如果抛出异常,原始对象至少需要一个强异常保证。
那么,如果在 move-construction 期间抛出异常会发生什么?这取决于子对象表现出的保证以及您组合调用的方式......
就异常保证而言,这意味着默认情况下,如果所有子对象的构造函数至少满足Basic Exception Guarantee,那么您的移动构造函数也将满足,无需特别注意。
但是,即使所有子对象的构造函数都满足强异常保证,您也不太可能成功地让自己的移动构造函数满足它:这与链接事务不会产生事务的问题相同。
如果只有一个子对象的构造函数可以抛出,并且它满足强异常保证,那么如果你先初始化抛出对象,你的移动构造函数自然会遇到它。
希望这会有所帮助......例外是驯服的野兽:)