throw() 函数是否应该始终在出现异常时展开堆栈并允许捕获异常或必须调用 std::terminate?

Should throw() function always unwind stack on exception and allow exception to be catched or std::terminate must be called?

我很想知道这是否是标准强制执行的,是否被某些编译器违反了。我的观察是:

Test code:

#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 的说明进行编辑。