簡體   English   中英

默認構造函數是否始終初始化所有成員?

[英]Does a default constructor always initialize all members?

我發誓我不記得以前見過這個,我很難相信自己的眼睛:

非聚合類的隱式定義的默認構造函數是初始化其成員還是否?

在Visual C ++中,當我運行這個看似無辜的代碼時......

#include <string>
struct S { int a; std::string b; };
int main() { return S().a; }

...令我驚訝的是,它返回一個非零值! 但是如果我刪除字段b ,那么它返回零。

我已經在VC ++的所有版本上嘗試了這個,我可以得到它,它似乎在所有這些上都這樣做。

但是當我在Clang和GCC上嘗試它時,無論我是在C ++ 98模式還是C ++ 11模式下嘗試它,都會將值初始化為零。

什么是正確的行為? 它不能保證為零嗎?

引用C ++ 11:

5.2.3顯式類型轉換(功能表示法)[expr.type.conv]

2表達式T() ,其中T是非數組完整對象類型的簡單類型說明符類型名稱說明 ,或者(可能是cv限定的) void類型,創建指定類型的prvalue,它是值-initialized(8.5;沒有為void()情況進行初始化)。 [...]

8.5初始值設定項[dcl.init]

7要初始化 T類型的對象,意味着:

  • ...
  • 如果T是一個(可能是cv限定的)非聯合類類型而沒有用戶提供的構造函數,那么該對象是零初始化的,如果T的隱式聲明的默認構造函數是非平凡的,則調用該構造函數。
  • ...

因此,在C ++ 11 S().a應為零:對象是零初始化的構造函數被調用之前,和構造從來沒有價值的變化a到別的。

在C ++ 11之前,值初始化具有不同的描述。 引用N1577(大致是C ++ 03):

類型T的對象進行值初始化意味着:

  • ...
  • 如果T是沒有一個用戶聲明的構造的非聯合類型,那么每個非靜態數據成員和基礎類組分T是值初始化;
  • ...
  • 否則,該對象被零初始化

這里, S值初始化沒有調用任何構造函數,而是導致其ab成員的值初始化。 然后, a成員的值初始化導致該特定成員的零初始化。 在C ++ 03中,結果也保證為零。

甚至比這更早,轉到第一個標准,C ++ 98:

表達式T() ,其中T是非數組完整對象類型的簡單類型說明符 (7.1.5.2)或(可能是cv限定的) void類型,它創建指定類型的右值,其值為由default-initialization確定(8.5;沒有為void()情況進行初始化)。

默認初始化 T類型的對象意味着:

  • 如果T是非POD類類型(第9節),則調用T的默認構造函數(如果T沒有可訪問的默認構造函數,則初始化是錯誤的);
  • ...
  • 否則,對象的存儲被零初始化。

所以基於第一個標准,VC ++是正確的:當你添加一個std::string成員時, S變成非POD類型,而非POD類型不會得到零初始化,它們只是調用它們的構造函數。 對於隱式生成默認構造函數S不初始化a成員。

因此,只需遵循標准的不同版本,所有編譯器都可以說是正確的。

正如評論報道@Columbo,后來VC ++的版本並導致a成員按照較新版本的C ++標准來進行初始化。

(第一部分中的所有引用均來自N3337,C ++ 11 FD,編輯修改)

我無法在rextester上重現VC ++的行為。 據推測,這個bug(見下文)已在他們正在使用的版本中修復,但不在你的版本中 - @Drop報告最新版本VS 2013 Update 4未通過斷言 - 而VS 2015預覽通過它們。

只是為了避免誤解: S確實是一個集合。 [dcl.init.aggr] / 1:

聚合是一個數組或類(第9條),沒有用戶提供的構造函數(12.1),沒有私有或受保護的非靜態數據成員(第11條),沒有基類(第10條),沒有虛函數(10.3) )。

但這無關緊要。
值初始化的語義很重要。 [dcl.init] / 11:

初始值為空集括號的對象,即() ,應進行值初始化。

[dcl.init] / 8:

類型T的對象進行值初始化意味着:

  • 如果T是一個(可能是cv限定的)類類型(第9條),沒有默認構造函數(12.1)或者是用戶提供或刪除的默認構造函數,那么該對象是默認初始化的;
  • 如果T是一個(可能是cv限定的)類類型而沒有用戶提供或刪除的默認構造函數,那么該對象是零初始化的,並且檢查默認初始化的語義約束,如果T有一個非平凡的默認構造函數,該對象是默認初始化的;
  • [..]

顯然,無論b是否在S這都成立。 因此,至少在C ++ 11在這兩種情況下a應該是零。 Clang和GCC顯示正確的行為


現在讓我們來看看C ++ 03 FD:

類型T的對象進行值初始化意味着:

  • 如果T是具有用戶聲明的構造函數(12.1)的類類型(第9節)[...]
  • 如果T是沒有一個用戶聲明的構造的非聯合類型,那么每個非靜態數據成員和基礎類組分T是值初始化;
  • 如果T是數組類型,那么每個元素都是值初始化的;
  • 否則,該對象被零初始化

也就是說,即使在C ++ 03中(其中[dcl.init] / 11中的上述引用也存在於/ 7中),在兩種情況下a都應為0
同樣, GCC和Clang都是正確的-std = c ++ 03。

hvd的回答所示,您的版本僅適用於C ++ 98和C ++ 98。

暫無
暫無

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

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