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

Using `throw;` on a modified exception

本文关键字:throw 修改 异常      更新时间:2023-10-16

我有一个函数foo可以抛出bar异常。

在另一个函数中,我调用foo但我能够在抛出bar异常时添加更多详细信息。(我宁愿不将此类信息作为参数传递给foo,因为由于该函数的通用性质,它实际上不属于那里。

所以我在调用者中这样做:

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

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

(我知道我可以验证,但我担心偶然发现未指定或未定义的结构,所以想知道(。

实际上,这里的标准非常精确。[除了句柄]/17:

当处理程序声明对非常量对象的引用时,任何 对引用对象的更改是对临时对象的更改 在执行抛出表达式时初始化,并将具有 如果重新抛出该对象,效果

和[除了投掷]/8:

没有操作数的抛出表达式会重新抛出当前处理的 异常 (15.3(。

C++11 §15.1/8:

"> 不带操作数的抛出表达式将重新引发当前处理的异常 (15.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 捕获的"异常",即它会再次复制。因此,只需忘记您所做的更改,它甚至不会将或*ginal异常传递给调用者。它会向调用方例程抛出"异常"。

希望我清楚(并且正轨C++标准(足够...

throw(没有异常对象(将重新抛出当前异常。(必须在 catch 块内,否则调用 std::终止(。由于您更改了当前异常对象的引用,因此无需显式抛出该对象并重新抛出修改后的异常,并且不会创建新的临时对象。

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

  1. 抛出表达式:首先,从表达式复制初始化异常对象(这可能会调用 rvalue 表达式的移动构造函数,并且复制/移动可能会受到复制省略的影响(,然后将控制权转移到具有匹配类型的异常处理程序,该类型的复合语句或成员初始值设定项列表最近输入且未被此执行线程退出。
  2. throw:重新引发当前处理的异常。放弃当前 catch 块的执行并将控制权传递给下一个匹配的异常处理程序(但不传递给同一 try 块之后的另一个 catch 子句:其复合语句被视为已"退出"(,重用现有的异常对象:不创建新对象。仅当当前正在处理异常时,才允许使用此表单(如果以其他方式使用,则调用 std::terminate (。如果在构造函数上使用,则与函数 try-block 关联的 catch 子句必须通过重新抛出退出。

所以为了强调我的回答,投掷在你的情况下应该没问题。