[英]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
值初始化沒有調用任何構造函數,而是導致其a
和b
成員的值初始化。 然后, 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.