簡體   English   中英

在 C++11 中拋出異常時是否使用移動語義?

[英]Does an exception use move semantics when thrown in C++11?

http://www.drdobbs.com/cpp/practical-c-error-handling-in-hybrid-env/197003350?pgno=4

在這篇文章中,Herb Sutter 解釋了拋出異常需要一個異常的副本,因為它是作為臨時創建的,因此使用std::auto_ptr來繞過復制開銷。 鑒於 C++11 中提供了移動語義,這仍然是必要的嗎?

我剛剛檢查過,標准允許

  • 省略將 throw 表達式的操作數指定的對象復制或移動到異常對象中
  • 如果您不以其他方式更改程序的含義(即,如果您重新拋出並且隨后的捕獲會突然看到更改的異常對象),則省略將異常對象復制或移動到與異常對象相同類型的 catch 子句變量中由前一個 catch 塊更改)。

由於這些遺漏是允許的,規范要求首先將復制或移動的來源視為右值。 因此,這意味着如果可能,移動相應的對象。 當然,復制和移動省略仍然是首選。


更新

我被告知,將 catch 子句參數的異常對象初始值設定項作為右值初始值設定項的考慮可能會從標准中刪除(因為通常情況下,不可能在所有情況下都檢測到在省略時程序的行為何時保持不變)復制/移動),所以我建議不要依賴這個(上面的第二個項目)。

您仍然可以依賴的是將局部變量移動到異常對象中,如throw x; (上面的第一個項目符號)。

從異常對象移動現在不是強制性的。

這是 C++11 的缺陷。 參見CWG1493

  • 在拋出表達式時,總是需要創建異常對象的副本,因為在堆棧展開過程中原始對象超出范圍。
  • 在初始化期間,我們可能會期望復制省略(請參閱this)——省略復制或移動構造函數(對象直接構造到目標對象的存儲中)。
  • 但是,即使可能會或可能不會應用復制省略,您也應該提供正確的復制構造函數和/或移動構造函數,這是 C++ 標准要求的(參見 15.1)。 請參閱下面的編譯錯誤以供參考。

但是現代 C++ 提供了更多功能:“使用移動語義和異常安全移動”

struct demo
{
    demo() = default;
    demo(const demo &) { cout << "Copying\n"; }
    // Exception safe move constructor
    demo(demo &&) noexcept { cout << "Moving\n"; }
private:
    std::vector<int>    m_v;
};
int main()
{
    demo obj1;
    if (noexcept(demo(std::declval<demo>()))){  // if moving safe
        demo obj2(std::move(obj1));             // then move it
    }
    else{
        demo obj2(obj1);                        // otherwise copy it
    }
    demo obj3(std::move_if_noexcept(obj1));     // Alternatively you can do this----------------
    return 0;
}
  • 我們可以使用noexcept(T(std::declval<T>()))以檢查是否T的移動構造函數存在且noexcept ,以決定是否我們要創建的實例T通過移動的另一個實例T (使用std::move )。
  • 或者,我們可以使用std::move_if_noexcept ,它使用noexcept運算符並轉換為右值或左值。 此類檢查用於std::vector和其他容器。
  • 這在您處理不想丟失的關鍵數據時非常有用。 例如,我們從服務器接收到關鍵數據,我們不想在處理時不惜一切代價丟失它。 在這種情況下,我們應該使用std::move_if_noexcept ,它僅在移動構造函數是異常安全的情況下才移動關鍵數據的所有權。

來自: C++ 異常處理的 7 個最佳實踐

暫無
暫無

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

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