在修改后的异常上使用`throw;`

时间:2014-11-12 09:38:02

标签: c++ exception exception-handling language-lawyer throw

我有一个函数foo可以抛出bar例外。

在另一个函数中,我调用了foo,但是如果抛出,我可以向bar异常添加更多细节。 (我宁愿不将这些信息作为参数传递给foo,因为它不属于那里,因为该函数的通用性质。)

所以我在调用者中执行此操作:

try {
    foo();
} catch (bar& ex){
    ex.addSomeMoreInformation(...);
    throw;
}

throw会重新抛出修改过的异常,还是需要使用throw ex;?后者可能会采用有价值的副本,所以我宁愿不这样做。 throw也会获取价值副本吗?我怀疑它不会。

(我知道我可以验证,但我担心会遇到一个未指定或未定义的构造,因此我想知道这一点。)

5 个答案:

答案 0 :(得分:58)

实际上,这里的标准非常精确。 [except.handle] / 17:

  

当处理程序声明对非常量对象的引用时, any   对引用对象的更改是对临时对象的更改   执行 throw-expression 时初始化并且将具有   效果应该重新抛出该物体

和[except.throw] / 8:

  

没有操作数的 throw-expression 重新抛出当前处理的内容   例外(15.3)。

答案 1 :(得分:27)

C ++11§15.1/ 8:

  

throw-expression 没有操作数会重新抛出当前处理的异常(15.3)。例外是   用现有的临时重新激活;没有创建新的临时异常对象。

答案 2 :(得分:3)

throw(没有异常对象)将重新抛出当前异常。 (必须在catch块内,否则调用std::terminate)。因为您更改了当前异常对象的引用,所以您不需要使用explicilty抛出该对象,并且抛出重新抛出修改后的异常,并且不会创建新的临时对象。

答案 3 :(得分:3)

在这种情况下,您应该使用throw来获得所需的行为...即,抛出将抛出修改后的异常,因为引用捕获了异常。

让我试着通过例子明确区分它们: -

class exception
{
};

class MyException : public exception
{
};

void func()
{
  try
  {
    throw MyException();
  }
  catch( exception& e )
  {
    //do some modification.
    throw;                    //Statement_1
    throw e;                  //Statement_2
   }
}

Statment_1: -

抛出的是它只是重新抛出当前的异常,即它不会进行进一步的复制(就像最初抛出异常时所做的那样)。因此,如果您在此处对捕获的异常进行任何更改...它也会出现在调用程序例程中。

Statement_2: -

这是抛出最初被捕获为MyException的“异常”,即它会再次复制。所以,只要忘记你所做的改变甚至不会传递给调用者或者*主要的异常。它会向调用者例程抛出“异常”。

希望我很清楚(并且正确地遵循C ++标准)足够......

答案 4 :(得分:2)

根据this,在c ++中抛出异常可以通过两种方式完成:

  1. 抛出 表达式:首先,从表达式中复制初始化异常对象(这可能会调用rvalue表达式的移动构造函数,并且复制/移动可能受制于复制elision),然后将控制转移到异常处理程序,其匹配类型的复合语句或成员初始化列表最近输入,而不是由此执行线程退出。
  2. throw :重新排列当前处理的异常。放弃当前catch块的执行并将控制传递给下一个匹配的异常处理程序(但不会在同一个try块之后传递给另一个catch子句:它的compound-statement被认为已经'退出'),重用现有的异常对象:没有新的对象。此表单仅在当前正在处理异常时允许(如果另外使用则调用std :: terminate)。如果在构造函数上使用,则与函数try-block关联的catch子句必须通过rethrowing退出。
  3. 所以为了强调我的答案, throw 应该没问题。