try-catch-rethrow的代码是否等同于没有try-catch的代码?

Wal*_*ter 23 c++ exception try-catch throw language-lawyer

在哪种情况下,以下两个代码不相同?

{
  // some code, may throw and/or have side effects
}

try {
  // same code as above
} catch(...) {
  throw;
}
Run Code Online (Sandbox Code Playgroud)

编辑只是为了澄清,我对(i)偏离上述模式(例如catch块中的更多代码)以及(ii)旨在邀请关于try- catchblock 的正确使用的光顾评论感兴趣.

我正在寻找一个参考C++标准的合格答案.这个问题是由干杯和他的评论提出的.- Alf对我的这个答案,说明没有进一步说明上述代码不相同.


编辑他们确实不同.堆栈取消将在后者中完成,但不一定在前者中,取决于是否catch在运行时找到异常处理程序(堆栈上方的某些块).

Tar*_*ama 17

后者要求堆栈展开,而在前者中,如果堆栈展开则它是实现定义的.

相关标准报价(全部来自N3337):

[except.ctor]/1:当控制从throw-expression传递到处理程序时,将为输入try块后构造的所有自动对象调用析构函数.自动对象按照完成构造的相反顺序销毁.

[except.ctor]/3: 为从try块到throw-expression的路径构造的自动对象调用析构函数的过程称为"堆栈展开".[...]

[except.terminate]/2:[当异常处理机制找不到抛出异常的处理程序时],std::terminate()调用(18.8.3).在没有找到匹配处理程序的情况下,无论堆栈是否在std::terminate()被调用之前被展开,它都是实现定义的.[...]

因此,如果你想保证你的自动对象在未处理的异常的情况下运行它们的析构函数(例如,某些持久存储必须在销毁时变异),那么try {/*code*/} catch (...) {throw;}就会这样做,但{/*code*/}不会.

  • @Walter我使用clang在翻译单元中测试了这个,而`{/*code*}`没有调用析构函数,但重新抛出版本没有.关于异常实现,我不太了解它是如何完成的. (2认同)

Pet*_*etr 7

关于干杯和赫斯的Elaboratring.- 阿尔夫的评论:

来自http://en.cppreference.com/w/cpp/error/terminate:

当异常处理由于以下任何原因而失败时,C++运行时调用std :: terminate():

1)抛出异常并且没有捕获(在这种情况下是否执行任何堆栈展开是实现定义的)

因此,如果你的话,堆栈展开可能不会发生

{
  // some code, may throw and/or have side effects
}
Run Code Online (Sandbox Code Playgroud)

不在另一个try/catch街区内.

例:

struct A {
    A() {}
    ~A() { std::cout << "~A()" << std::endl; }
};

int main()
{
//    try {
        A a;
        throw 1;
//    } catch(...) {
//        throw;
//    }
}
Run Code Online (Sandbox Code Playgroud)

在coliru的gcc 5.2.0下,-O2不打印~A(),而打印时try/catch则打印.

UPD:关于你对单独的编译单元的编辑,只是用我的本地gcc 4.8.2测试,行为是相同的:没有堆栈展开,如果没有catch.特定的例子:

a.h:

struct A {
   A();
   ~A();
};

void foo();
Run Code Online (Sandbox Code Playgroud)

a.cpp:

#include <iostream>
using namespace std;

struct A {
   A() {}
   ~A() { cout << "~A()" << endl; }
};

void foo() {
    A a;
    throw 1;
}
Run Code Online (Sandbox Code Playgroud)

main.cpp:

#include "a.h"

int main () {
   //try {
    foo();
   //} catch(...) {
   //  throw;
   //}
}
Run Code Online (Sandbox Code Playgroud)

我认为是否有一个catch是在运行时确定的,因为无论如何在运行时抛出异常,程序需要寻找catch.因此,选择是否在运行时解除堆栈也是有意义的.

  • @ user2079303,甚至更简单:关闭文件输出流,以便缓冲的所有数据实际上都会击中磁盘. (3认同)