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),但异常退出函数并被 [=31 捕获=].没有调用 std::terminate。
gcc (6.3.0),throw() 函数中的堆栈被展开,但随后 std::terminate 被调用(try/catch 无法排除异常)。但是对于 7.0(当前 HEAD),没有堆栈被展开, std::termiante 被立即调用。
实际上 gcc 7.0 甚至会警告:warning: throw will always call terminate() [-Wterminate]
for NoExceptFunctionWithObj2()
。它使 throw() 表现得像 noexcept(true).
clang,在所有版本中我都检查了 unwinds 函数堆栈(调用了对象 d-tor),然后调用了 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() functions\n";
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
。标准的 latest published draft 表示:
15.4 [except.spec],第 12 段
An
exception-specification
is
non-throwing
if it is of the form
throw()
,
noexcept
, or
noexcept(
constant-expression
)
where the
constant-
expression
yields
true
.
这意味着 throw()
严格等同于 noexcept(true)
。 throw()
在 C++17
中被弃用。
15.5.1 [except.terminate]
when the search for a handler (15.3) encounters the outermost block of a function with a
noexcept-
specification
that does not allow the exception (15.4) [...] std::terminate()
is called (18.8.3). In the situation where no matching handler is found,
it is implementation-defined whether or not the stack is unwound before
std::terminate()
is called
不调用 std::terminate
意味着 MSVC 不兼容。
关于堆栈的处理,在您的示例中,编译器会执行它想要展开或不展开的操作 — 这被指定为 实现定义的。
历史上(在 C++11 之前)在这种情况下堆栈展开是强制性的。然而,事实证明,这种强制行为的运行时成本太高,并且它抑制了编译器进行一些优化(即使在非异常情况下)。因此,编译器现在可以自由地忽略它。
根据@mike 的说明进行编辑。
我很想知道这是否是标准强制执行的,是否被某些编译器违反了。我的观察是:
- Visual Studio 2015/2017(使用 /EHsc):throw() 函数中的堆栈未展开(未调用 d-tor),但异常退出函数并被 [=31 捕获=].没有调用 std::terminate。
gcc (6.3.0),throw() 函数中的堆栈被展开,但随后 std::terminate 被调用(try/catch 无法排除异常)。但是对于 7.0(当前 HEAD),没有堆栈被展开, std::termiante 被立即调用。 实际上 gcc 7.0 甚至会警告:
warning: throw will always call terminate() [-Wterminate]
forNoExceptFunctionWithObj2()
。它使 throw() 表现得像 noexcept(true).clang,在所有版本中我都检查了 unwinds 函数堆栈(调用了对象 d-tor),然后调用了 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() functions\n";
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
。标准的 latest published draft 表示:
15.4 [except.spec],第 12 段
An exception-specification is non-throwing if it is of the form throw() , noexcept , or noexcept( constant-expression ) where the constant- expression yields true .
这意味着 throw()
严格等同于 noexcept(true)
。 throw()
在 C++17
中被弃用。
15.5.1 [except.terminate]
when the search for a handler (15.3) encounters the outermost block of a function with a noexcept- specification that does not allow the exception (15.4) [...] std::terminate() is called (18.8.3). In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate() is called
不调用 std::terminate
意味着 MSVC 不兼容。
关于堆栈的处理,在您的示例中,编译器会执行它想要展开或不展开的操作 — 这被指定为 实现定义的。
历史上(在 C++11 之前)在这种情况下堆栈展开是强制性的。然而,事实证明,这种强制行为的运行时成本太高,并且它抑制了编译器进行一些优化(即使在非异常情况下)。因此,编译器现在可以自由地忽略它。
根据@mike 的说明进行编辑。