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

时间:2017-05-10 12:13:15

标签: c++ c++11 exception exception-handling compiler-bug

鉴于此代码:

template <typename lhsT, typename rhsT>
auto add(lhsT& lhs, rhsT& rhs) -> decltype(lhs + rhs) {
  return lhs +rhs;
}

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

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;
    }
}

但是在使用Clang(版本4.0.0)进行编译时:

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

哪种行为是正确的?

2 个答案:

答案 0 :(得分:10)

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

如果构造函数是try/catch子句中的第一个op,那么编译器认为它在它之外,尽管它应该包含它。

例如,以下工作正常:

#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

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

如果我们将struct B的声明更改为明确,请使用struct A构造函数:

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

然后结果将符合预期,即使删除“欢迎”打印输出,我们也会输入try/catch

ERROR: 42
0

答案 1 :(得分:4)

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

  

15.3处理异常[except.handle]

     

...
3如果是,则处理程序匹配E类型的异常对象   (3.1) - 处理程序的类型为cv T或cv T&amp;和E和T是相同的类型(忽略顶级cv限定符),

这里抛出int,处理程序声明const int。有匹配,应该调用处理程序。