如果从类成员初始值设定项引发的异常调用 std::terminate()



给定此代码:

struct A {
    A(int e) { throw e; }
};
struct B {
    A a{42}; // Same with = 42; syntax
};
int main() {
    try {
        B b;
    } catch (int const e) {
        return e;
    }
}

使用 GCC 编译时(版本 4.7.4、4.8.5、4.9.3、5.4.0、6.3.0(:

$ g++ -std=c++11 test.cpp -o test; ./test ; echo $?
terminate called after throwing an instance of 'int'
Aborted
134

但是当使用 Clang 编译(版本 4.0.0(时:

$ clang++ -std=c++11 test.cpp -o test; ./test ; echo $?
42

哪种行为是正确的?

这是 GCC 中的一个错误(错误 80683(。

如果构造函数是 try/catch 子句中的第一个操作,则编译器认为它在其外部,尽管它应该包含它。

例如,以下工作正常:

#include <iostream>
struct A {
    A(int e) { throw e; }
};
struct B {
    A a{42}; // Same with = 42; syntax
};
int main() {
    try {
        // The following forces the compiler to put B's contructor inside the try/catch.
        std::cout << "Welcome" << std::endl; 
        B b;
    } catch (int e) {
        std::cout << "ERROR: " << e << std::endl; // This is just for debugging
    }
    return 0;
}

运行:

g++ -std=c++11 test.cpp -DNDEBUG -o test; ./test ; echo $?

输出:

Welcome
ERROR: 42
0

我的猜测是,由于编译器优化,它将构造函数移动到主函数的开头。它假设 struct B 没有构造函数,然后假设它永远不会抛出异常,因此将其移动到 try/catch 子句之外是安全的。

如果我们要将 struct B 声明更改为显式使用构造函数struct A

struct B {
    B():a(42) {}
    A a;
};

然后结果将符合预期,我们将进入 try/catch ,即使删除"欢迎"打印输出:

ERROR: 42
0

Clang 是正确的。有关这方面的参考资料可以在标准参考草案 n4296 中找到C++(强调我的(:

15.3 处理异常 [例外句柄]


3 处理程序是 E 类型的异常对象的匹配项,如果
(3.1( — 处理程序的类型为 cv T 或 cv T& 并且 E 和 T 是同一类型(忽略顶级 cv 限定符(,

这里抛出了一个int,处理程序声明了一个const int。存在匹配项,应调用处理程序。

相关内容

最新更新