使用arg捕获的异常抛出和抛出有什么区别?

Wil*_*mKF 21 c++ exception try-catch throw

想象一下两个相似的代码片段:

try {
  [...]
} catch (myErr &err) {
  err.append("More info added to error...");
  throw err;
}
Run Code Online (Sandbox Code Playgroud)

try {
  [...]
} catch (myErr &err) {
  err.append("More info added to error...");
  throw;
}
Run Code Online (Sandbox Code Playgroud)

这些是否实际相同,或者它们是否以某种微妙的方式不同?例如,第一个是否导致复制构造函数运行,而第二个可能重用相同的对象来重新抛出它?

CB *_*ley 28

根据排列异常层次结构的方式,通过在throw语句中命名异常变量来重新抛出异常可能会对原始异常对象进行切片.

无参数抛出表达式将抛出当前异常对象,保留其动态类型,而带有参数的throw表达式将根据参数的静态类型抛出新异常throw.

例如

int main()
{
    try
    {
        try
        {
            throw Derived();
        }
        catch (Base& b)
        {
            std::cout << "Caught a reference to base\n";
            b.print(std::cout);
            throw b;
        }
    }
    catch (Base& b)
    {
        std::cout << "Caught a reference to base\n";
        b.print(std::cout);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如上所述,程序将输出:

Caught a reference to base
Derived
Caught a reference to base
Base

如果throw b替换为a throw,则外部catch也将捕获最初抛出的Derived异常.如果内部类Base通过值而不是通过引用捕获异常,这仍然成立- 尽管这自然意味着原始异常对象不能被修改,因此任何更改b都不会反映在Derived外部块捕获的异常中.


Kir*_*sky 16

在根据C++标准15.1/6的第二种情况下,不使用复制构造函数:

没有操作数的throw-expression重新抛出正在处理的异常.使用现有临时值重新激活该例外; 没有创建新的临时异常对象.该例外情况不再被视为被捕; 因此,uncaught_exception()的值将再次为真.

在第一种情况下,将根据15.1/3抛出新异常:

throw-expression初始化一个临时对象,称为异常对象,其类型是通过从throw的操作数的静态类型中删除任何顶级cv限定符并从"T的数组"调整类型来确定的.函数返回T"到"指向T"的指针或"指向函数返回T的指针".<...>临时用于初始化匹配处理程序(15.3)中指定的变量.throw-expression的类型不应是不完整类型,也不应是指向不完整类型的指针或引用,而不是void*,const void*,volatile void*或const volatile void*.除了这些限制和15.3中提到的类型匹配限制之外,throw的操作数被完全视为调用(5.2.2)中的函数参数或return语句的操作数.

在这两种情况下,在throw阶段(15.1/5)都需要复制构造函数:

当抛出的对象是类对象,并且无法访问用于初始化临时副本的复制构造函数时,程序格式错误(即使临时对象可能被消除).类似地,如果该对象的析构函数不可访问,则程序格式错误(即使临时对象可能被消除).