2015-02-09 119 views
22

我不明白C++ 11大括號初始化規則是如何在這裏工作的。 有了這個代碼:C++ 11奇怪大括號初始化行爲

struct Position_pod { 
    int x,y,z; 
}; 

class Position { 
public: 
    Position(int x=0, int y=0, int z=0):x(x),y(y),z(z){} 
    int x,y,z; 
}; 

struct text_descriptor { 
    int    id; 
    Position_pod pos; 
    const int  &constNum; 
}; 

struct text_descriptor td[3] = { 
    {0, {465,223}, 123}, 
    {1, {465,262}, 123}, 
}; 

int main() 
{ 
    return 0; 
} 

筆記,該陣列被聲明爲具有3個元素,但僅提供2初始化。

然而,它編譯沒有錯誤,這聽起來很奇怪,因爲最後一個數組元素的引用成員將被初始化。事實上,它具有NULL值:

(gdb) p td[2].constNum 
$2 = (const int &) @0x0: <error reading variable> 

而現在的 「魔術」:我改變Position_pod到位置

struct text_descriptor { 
    int    id; 
    Position_pod pos; 
    const int  &constNum; 
}; 

變成這樣:

struct text_descriptor { 
    int    id; 
    Position  pos; 
    const int  &constNum; 
}; 

,現在,它給出了預期的錯誤:

error: uninitialized const member ‘text_descriptor::constNum' 

我q問題:爲什麼它在第一種情況下編譯,何時應該給出錯誤(如第二種情況)。 區別在於,Position_pod使用C風格的大括號初始化,而Position使用C++ 11風格的初始化,它調用Position的構造函數。但是,這會如何影響將參考成員未初始化的可能性?

(更新) 編譯: GCC(Ubuntu的4.8.2-19ubuntu1)4.8.2

+9

編譯器版本很重要。 [Clang 3.5和GCC 4.9.2](http://coliru.stacked-crooked.com/a/4669040f8cc1bd3e)不要編譯這個。 – 2015-02-09 11:44:26

+1

看起來像一個編譯器錯誤。我可以在GCC 4.8和更早的版本上重現,因此看起來它已經在4.9版本中得到了修復。 – 2015-02-09 11:49:42

+1

MSVC 2013不會編譯。 – Drop 2015-02-09 11:54:29

回答

23

很明顯的是

struct text_descriptor td[3] = { 
    {0, {465,223}, 123}, 
    {1, {465,262}, 123}, 
}; 

是列表初始化,並且初始化器列表不爲空。

C++ 11表示([dcl.init.list] P3):

List-initialization of an object or reference of type T is defined as follows:

  • If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
  • Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1).
  • ...

[dcl.init.aggr] P1:

An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal-initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).

td是一個數組,所以它是一個聚合,所以執行聚合初始化。

[dcl.init.aggr] P7:

If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list (8.5.4).

這是這裏的情況,所以td[2]被從空初始化劑列表,其中([dcl.init.list再次P3)初始化意味着它是價值初始化的。

價值初始化,進而,裝置([dcl.init] P7):

To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), ...
  • if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T 's implicitly-declared default constructor is non-trivial, that constructor is called.
  • ...

text_descriptor類是沒有用戶提供的構造函數的類,所以td[2]是第一零初始化,並然後調用它的構造函數。

零初始化裝置([dcl.init] P5):

To zero-initialize an object or reference of type T means:

  • if T is a scalar type (3.9), ...
  • if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;
  • if T is a (possibly cv-qualified) union type, ...
  • if T is an array type, ...
  • if T is a reference type, no initialization is performed.

這無論text_descriptor的默認構造的良好定義的:它只是零初始化非基準構件和子成員。

然後默認構造函數被調用,如果它不平凡。這裏的默認構造函數是如何定義的([特殊] P5):

A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared constructor for class X , a constructor having no parameters is implicitly declared as defaulted (8.4). An implicitly-declared default constructor is an inline public member of its class. A defaulted default constructor for class X is defined as deleted if:

  • ...
  • any non-static data member with no brace-or-equal-initializer is of reference type,
  • ...

A default constructor is trivial if it is not user-provided and if:

  • its class has no virtual functions (10.3) and no virtual base classes (10.1), and
  • no non-static data member of its class has a brace-or-equal-initializer, and
  • all the direct base classes of its class have trivial default constructors, and
  • for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.

Otherwise, the default constructor is non-trivial.

因此,隱式定義構造函數被刪除,如預期,它也是微不足道的,如果pos是一個POD類型(!) 。因爲構造函數很簡單,所以不會調用它。因爲構造函數沒有被調用,所以它被刪除的事實不成問題。

這是C++ 11中的一個漏洞,它已經被修復。它恰巧已經被修復以處理inaccessible trivial default constructors,但固定的措詞也涵蓋了被刪除的簡單默認構造函數。 N4140(大致C++ 14)在[dcl.init.aggr] p7(強調我的)中說:

  • if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;

正如T.C.在評論中指出,another DR也發生了變化,因此td[2]仍然是從空的初始化程序列表初始化的,但是空的初始程序列表現在意味着聚合初始化。這又意味着每個td[2]的成員也會從空的初始化列表中初始化(再次[dcl.init.aggr] p7),所以似乎從{}初始化引用成員。

[dcl.init.aggr] P9接着說(如remyabel已經在現在刪除的答案指出):

If an incomplete or empty initializer-list leaves a member of reference type uninitialized, the program is ill-formed.

目前還不清楚,我認爲這也適用於從隱性{}初始化引用,但是編譯器確實這樣解釋它,並沒有其他的東西可以用它來表示。

+0

引用不能被初始化值。 – 2015-02-09 12:31:52

+0

@remyabel我知道,但是一個類的值初始化並不意味着該類成員的價值初始化。 – hvd 2015-02-09 12:32:23

+0

@bogdan它有一個默認的構造函數。該默認構造函數被刪除,因此如果使用該默認構造函數時會出錯,但由於措辭上的差距,甚至無法使用。我從標準中引用了[特殊] p5,它指定何時定義默認構造函數,以及如何定義它。 – hvd 2015-02-09 12:40:02

-1

第一個版本(帶有_pod後綴)仍然有效,但沒有給出錯誤,因爲z值時, int的默認值被選中(0)。同樣是const int引用。

在第二個版本中,你不能定義一個const引用而不給它一個值。編譯器給你一個錯誤,因爲以後你不能給它賦值。

另外,你使用的編譯器在這裏起着重要的作用,也許這是一個錯誤,只是因爲你是在int成員之前聲明一個類成員。

+2

但是兩個版本(嘗試)初始化一個引用而不給它一個值。問題是關於數組初始化的數量,而不是每個數組元素的「位置」成員。 (並回答您的問題:演示如果將該成員的類型從POD更改爲非POD,編譯器的行爲將發生怎樣的變化。) – 2015-02-09 11:48:34

+0

@MikeSeymour這是一個編譯器問題。你也包括一個結構類。如果你保持結構容易,或者在第一個結構 – madduci 2015-02-09 11:51:19

+1

「這是一個編譯器問題」中使用構造函數會更好。但是你的回答並沒有這麼說,而且似乎認爲第一個版本是完整的,即使它不是。 – 2015-02-09 11:52:07