簡體   English   中英

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-tors),但是異常退出了功能並被try / catch捕獲。 沒有調用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]NoExceptFunctionWithObj2() warning: throw will always call terminate() [-Wterminate] 它使throw()的行為與noexcept(true)相同。

  • 在所有版本中,我都檢查過展開函數堆棧(調用了對象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() 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 最新發布的標准草案說:

15.4 [規格除外],第12段

如果異常規范的形式為throw(),noexcept或noexcept(constant-expression),其中常量表達式產生true,則該異常規范為非拋出。

這意味着throw()嚴格等同於noexcept(true) C++17不推薦使用throw()

15.5.1 [終止除外]

在處理程序(15.3)的搜索遇到帶有noexcept-spec的函數的最外面的塊時,該函數不允許出現異常(15.4)[...] std :: terminate()(18.8.3)。 在找不到匹配處理程序的情況下,由實現定義是否在調用std :: terminate()之前取消堆棧的堆棧

不調用std::terminate表示MSVC不兼容。

關於堆棧的處理,在您的示例中,編譯器會執行它希望對其進行展開或不展開的操作-這被指定為實現定義的

歷史上(在C ++ 11之前)在這種情況下必須進行堆棧展開。 然而,事實證明,這種強制行為的運行時成本太高,並且它抑制了編譯器進行某些優化(即使在非異常情況下)。 結果,編譯器現在可以自由地將其省略。

經過@mike澄清后進行編輯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM