C ++移动语义和异常

时间:2011-01-19 05:37:22

标签: c++ exception c++11 move-semantics

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

原始物体会保留吗?或者是处于未定义状态的原始对象和移动对象?该语言提供的保证是什么?

3 个答案:

答案 0 :(得分:10)

我认为标准委员会最初试图让它变得如此移动构造者不会被允许抛出异常,但(至少截至今天)发现试图强制执行有太多陷阱。

提案N3050“允许移动构造者投掷(第1版)”已纳入标准草案。本质上,该提议增加了移动构造函数抛出的能力,但禁止“抛出”移动用于需要强大的异常安全保证的某些操作(如果非投掷移动不是,则库将回退到复制对象)可用)。

如果将移动构造函数标记为非抛出(noexcept)并抛出异常,则将调用std :: terminate()。

也许值得阅读David Abrahams撰写的博客文章,该文章讨论了N3050旨在解决的问题:

答案 1 :(得分:3)

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

对于被移动到的对象,它与从当前C ++中的ctor抛出相同:销毁任何构造的基础和成员,执行ctor的函数try handler,如果有的话,然后传播异常。详情见N3225§15.2p2。

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

  

分配器的这种移动结构不应通过例外退出。 [N3225§23.2p8]

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

答案 2 :(得分:2)

您的问题就等于关于例外保证的问题。有三种类型的例外保证(适用于功能):

  • 完全没有例外保证(不是真正的类型......但如果不关心此事可能会发生)
  • 基本例外保证:技术上正确,但功能不正确(即没有资源泄露,程序将在没有突然停止的情况下终止,但它可能会产生不良副作用,例如付款是兑现,但命令没有登记)
  • 强烈例外保证:全部或全无(如交易),即一切都做得好,或者我们回滚到之前的状态。
  • 不抛出异常保证:这不会抛出,所以不用担心。

编写函数时,通常会使用自己的保证来获取现有函数。很难增加例外保证,即您通常受到最弱保证的限制。

W.r.t你的问题,如果抛出异常,原始对象至少需要强异常保证

那么,如果在移动构建期间抛出异常会发生什么?这取决于子对象所展示的保证以及您组合呼叫的方式......

  1. 如果从构造函数抛出异常,则不会构建对象,并且会以相反的顺序销毁所有构建的子对象。此规则也适用于move-constructor
  2. 除非你在try catch中“包装”构造函数并以某种方式恢复已移动的对象,否则它们将丢失其资源。请注意,无论如何它们仍然必须处于可破坏状态,因此从技术上讲,程序将是正确的。
  3. 就异常保证而言,这意味着默认情况下,如果所有子对象的构造函数至少满足基本异常保证,那么您的移动构造函数也将无需特别注意。< / p>

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

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

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