throw() 函数应该总是在异常时展开堆栈并允许捕获异常还是必须调用 std::terminate ?
Should throw() function always unwind stack on exception and allow exception to be catched or std::terminate must be called?
我感兴趣的是这是否是标准强制的,以及某些编译器是否违反了它。我的观察是:
- Visual Studio 2015/2017(使用/EHsc):throw() 函数中的堆栈不会展开(不调用 d-tor),但异常会退出函数并被 try/catch 捕获。没有调用 std::终止。
-
gcc (6.3.0),throw() 函数中的堆栈被解开,但随后调用 std::terminate (try/catch 不能咳嗽异常)。但是在 7.0(当前 HEAD)中,没有堆栈被展开,std::termiante 立即被调用。 实际上 gcc 7.0 甚至警告了这一点:
warning: throw will always call terminate() [-Wterminate]
NoExceptFunctionWithObj2()
.它使 throw() 表现为 noexcept(true)。 -
clang,在所有版本中,我都检查了展开函数堆栈(调用对象 d-tors),然后调用 std::terminate 。
测试代码:
#include <iostream>
#include <string>
#include <vector>
struct TestDataWithoutNoexcept {
TestDataWithoutNoexcept() {
std::cout << __FUNCTION__ << "n";
}
~TestDataWithoutNoexcept() {
std::cout << __FUNCTION__ << "n";
}
TestDataWithoutNoexcept(TestDataWithoutNoexcept const & rhs) {
std::cout << __FUNCTION__ << "n";
}
TestDataWithoutNoexcept(TestDataWithoutNoexcept && rhs) {
std::cout << __FUNCTION__ << "n";
}
TestDataWithoutNoexcept& operator=(TestDataWithoutNoexcept const& rhs) {
std::cout << __FUNCTION__ << "n";
}
};
void NoExceptFunctionWithObj1() noexcept {
TestDataWithoutNoexcept test;
throw std::runtime_error("NoExceptFunctionWithObj1 ex.");
}
void NoExceptFunctionWithObj2() throw() {
TestDataWithoutNoexcept test;
throw std::runtime_error("NoExceptFunctionWithObj2 ex.");
}
int main()
{
// Now lets see whether stack is being unwound when exception is thrown in noexcept versus throw() function.
std::cout << "n See how dtors are called in noexcept or throw() functionsn";
try {
//NoExceptFunctionWithObj1();
}
catch (std::runtime_error& ex) {
std::cout << ex.what();
}
try {
NoExceptFunctionWithObj2();
}
catch (std::runtime_error& ex) {
std::cout << "nShouldn't this be shown? : " << ex.what();
}
}
是的,应该调用std::terminate
。最新公布的标准草案说:
15.4 [规格除外],第12段
一 异常规范 是 非投掷 如果是形式 投掷() , 否,除了 或 不,除了( 常量表达式 ) 其中 不断- 表达 收益 率 真 .
这意味着throw()
严格等同于noexcept(true)
。throw()
在C++17
中已弃用。
15.5.1 [终止除外]
当搜索处理程序 (15.3) 遇到函数的最外层块时,具有 不,除了—— 规范 不允许异常 (15.4) [...] std::terminate() 称为 (18.8.3)。在找不到匹配处理程序的情况下, 堆栈是否在之前解开都是实现定义的 std::terminate() 被称为
不调用std::terminate
意味着 MSVC 不合规。
关于堆栈的处理,编译器在您的示例中执行它想要的解开或不展开它 - 这被指定为实现定义的。
从历史上看(在 C++11 之前),在这种情况下必须进行堆栈平仓。然而,事实证明,这种强制行为的运行时成本太高,并且它抑制了编译器进行一些优化(即使在非异常情况下)。因此,编译器现在可以自由地将其排除在外。
经@mike澄清后编辑。
- 当类定义不可见时捕获异常
- 来自 Android 应用程序内部的 boost 类型的 boost::wrapexcept<boost::system::system_error> 的未捕获异常
- 如何通过 pybind11 从 python 中的C++中捕获异常?
- 信号后未捕获异常
- 捕获异常后如何退出程序执行
- C++ 捕获异常后进行清理的标准方法是什么?
- 使用模板类引发和捕获异常
- E/libc++abi:终止于类型为google::protobuf::FatalException的未捕获异常
- 如果在生成 std::thread 后引发,则未捕获异常
- C++ 未捕获异常,程序将终止并中止
- C++程序在第一次尝试时会给出垃圾,但如果它捕获异常并重试,则会给出适当的值
- 仅捕获异常就可以检测所有二进制文件在C 中读取错误是否足够
- 如何捕获 I/O 异常(确切地说是 I/O,而不是 std::exception)
- 为什么捕获异常播放允许尾括号
- throw() 函数应该总是在异常时展开堆栈并允许捕获异常还是必须调用 std::terminate ?
- 寻求与类型为 std::invalid_argument 的未捕获异常相关的运行时错误的建议: stoi:无转换
- 如何从调用函数中捕获异常
- 当用户在字符数组中输入整数值时捕获异常
- C++按值捕获异常时的示例是不好的
- 使用 -O2 或 -O3 标志编译时未捕获异常