tutorial - visual studio c++11




新的關鍵字= C++ 11中的默認值 (2)

我不明白為什麼我會這樣做:

struct S { 
    int a; 
    S(int aa) : a(aa) {} 
    S() = default; 
};

為什麼不直接說:

S() {} // instead of S() = default;

為什麼要為此添加新的關鍵字?


n2210提供了一些原因:

違約管理有幾個問題:

  • 構造函數的定義是耦合的; 聲明任何構造函數抑制默認的構造函數。
  • 析構函數的默認值對於多態類是不合適的,需要明確的定義。
  • 一旦違約被壓制,就沒有辦法重新生效。
  • 默認實現通常比手動指定的實現更高效。
  • 非默認實現是非平凡的,它會影響類型語義,例如使非類型的類型。
  • 沒有聲明一個(非平凡的)替代品,沒有辦法禁止特殊的成員職能或全球運營商。
type::type() = default;
type::type() { x = 3; }

在某些情況下,類的主體可以在不需要更改成員函數定義的情況下進行更改,因為默認情況下會隨附加成員的聲明而更改。

請參閱三規則與C ++ 11成為五規則?

請注意,移動構造函數和移動賦值運算符不會為顯式聲明任何其他特殊成員函數的類生成,將不會為顯式聲明移動構造函數或移動的類生成複制構造函數和復制賦值運算符賦值運算符,並且具有顯式聲明的析構函數和隱式定義的複制構造函數或隱式定義的複制賦值運算符的類將被視為棄用


在某些情況下,這是一個語義問題。 這對於默認的構造函數來說並不是很明顯,但對於其他編譯器生成的成員函數來說,這一點很明顯。

對於默認的構造函數,有可能使任何具有空體的默認構造函數被認為是一個簡單的構造函數的候選者,與使用=default相同。 畢竟,舊的空默認構造函數是合法的C ++

struct S { 
  int a; 
  S() {} // legal C++ 
};

大多數情況下,編譯器是否理解這個構造函數是無關緊要的(手動或編譯器)。

但是,將空函數體視為“默認”的嘗試完全針對其他類型的成員函數。 考慮拷貝構造函數:

struct S { 
  int a; 
  S() {}
  S(const S&) {} // legal, but semantically wrong
};

在上面的例子中,寫入空體的拷貝構造函數現在是錯誤的 。 它不再實際上複製任何東西。 這是一個非常不同的默認拷貝構造函數語義。 期望的行為需要你編寫一些代碼:

struct S { 
  int a; 
  S() {}
  S(const S& src) : a(src.a) {} // fixed
};

然而,即使有這種簡單的情況,編譯器負責驗證拷貝構造函數是否與它自己生成的拷貝構造函數相同,或者是為了看到拷貝構造函數是微不足道的 (相當於一個memcpy ,基本上)。 編譯器必須檢查每個成員初始化表達式,並確保它與表達式相同以訪問源的相應成員,而不是其他任何成員,確保沒有成員留下非平凡的默認構造等等。它是以過程的方式向後編譯器會用它來驗證它自己生成的這個函數的版本是微不足道的。

那麼考慮一下複製賦值運算符,它可以變得更加複雜,特別是在非平凡的情況下。 這是一大堆你不想為許多課程編寫的鍋爐板,但你仍然被迫在C ++ 03中:

struct T { 
  std::shared_ptr<int> b; 
  T(); // the usual definitions
  T(const T&);
  T& operator=(const T& src) {
    if (this != &src) // not actually needed for this simple example
      b = src.b; // non-trivial operation
    return *this;
};

這是一個簡單的例子,但它的代碼已經比你想要為T這樣一個簡單的類型編寫的代碼要多(特別是一旦我們將混合操作轉移到其他操作系統中)。 我們不能依靠一個空的身體意思是“填寫默認值”,因為空的身體已經完全有效並且有明確的含義。 事實上,如果空體被用來表示“填入默認值”,那麼就沒有辦法顯式地創建一個無操作拷貝構造函數等等。

這又是一個問題。 空洞的身體意味著“什麼也不做”,但對於復制構造者而言,你真的不希望“無所事事”,而是“做所有你通常會做的事情,如果不加抑制的話”。 因此=default 。 有必要克服壓縮編譯器生成的成員函數,如復制/移動構造函數和賦值運算符。 這對於使其在默認的構造函數中也是“明顯的”。

如果僅僅在某些情況下使舊代碼更優化,那麼使用空主體構造默認構造函數或者微不足道的成員/基構造函數也可能被認為是微不足道的,但是大多數低級代碼依賴在普通的默認構造函數上進行優化也依賴於普通的複制構造函數。 如果你將不得不去修復所有舊的構造函數,那麼修復所有舊的默認構造函數也不算什麼。 使用explicit =default來表示你的意圖,這也更加清楚和明顯。

編譯器生成的成員函數還會執行其他一些操作,您必須明確進行更改以支持。 默認構造函數的支持constexpr就是一個例子。 在使用=default比使用所有其他特殊關鍵字標記函數要簡單得多,並且=default隱含這些=default ,這是C ++ 11的主題之一:使語言更容易。 它仍然有大量的瑕疵和背後妥協的妥協,但很明顯,這是C ++ 03向易用性邁出的一大步。





c++11