2014-11-29 11 views
13

Se provo a compilare this codemembro Const impilare vs heap

struct A { 
    const int j; 
}; 
A a; 

prendo un errore previsto:

error: uninitialized const member in ‘struct A’

ma, se provo a compilare this one:

struct A { 
    const int j; 
}; 
A * a = new A(); 

Prendo una build di successo.

La domanda è: perché new allocazione permette di creare una variabile con membro const senza costruttore esplicito e l'allocazione dello stack - non?

+1

non viene compilato con GCC 4.9 o Clang. http: //coliru.stacked-crooked.it/a/fadd1a456bfd5b82 –

+0

@remyabel Che ovviamente non dice nulla sulla correttezza di questo codice. –

risposta

10

Non a causa dell'allocazione dell'heap, ma a causa della parentesi da utilizzare durante l'allocazione. Se lo fai, ad es.

A* a = new A; 

fallirebbe anche.

Il motivo per cui funziona quando si aggiunge la parentesi è perché allora la struttura è il valore inizializzato, e per un POD-tipo come A valore di inizializzazione valore inizializza ogni membro, e il valore di inizializzazione di default per int è zero.

Ciò significa che probabilmente funzionerebbe creare la variabile sullo stack pure, se si aggiunge solo la parentesi valore di inizializzazione:

A a = A(); // watch out for the http://en.wikipedia.org/wiki/Most_vexing_parse 

Benchè che porta altri problemi potenziali, meglio usare l'inizializzazione uniforme se potete (richiesta C++ 11):

A a{}; 
+6

'A a();' ovviamente, * mai * funzionerà. –

+1

Sì, si supponeva che fosse una dichiarazione di funzione. – ravi

+0

'A a = A();' funzionerebbe, ma 'A a {};' funziona davvero? – juanchopanza

6

Questo è mal formato a partire da C++ 14. §12.1 [class.ctor] dice che

4 A defaulted default constructor for class X is defined as deleted if:

  • [...]
  • any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer does not have a user-provided default constructor,
  • [...]

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.

§8.5 [dcl.init] dice a sua volta che

7 To default-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called (and the initialization is ill-formed if T has no default constructor or overload resolution (13.3) results in an ambiguity or in a function that is deleted or inaccessible from the context of the initialization);
  • [...]

8 To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is user-provided or deleted, then the object is default-initialized;
  • [...]

La coppia vuota di parentesi risultati di valore-inizializzazione. Il costruttore predefinito per A è definito come cancellato per [class.ctor]/p4. Così, di [dcl.init]/p8, valore-inizializzazione significa default-inizializzazione, e p7 dell'inizializzazione mal formata poiché il costruttore viene eliminato.

La versione C++ 11 effettivamente permesso valore di inizializzazione in questo contesto; si dice per il valore-inizializzazione che, tra le altre cose,

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.

Dal dalla definizione di cui sopra il costruttore di default è banale (anche se viene cancellato), non è in realtà chiamato. Questo è stato considerato un difetto nella norma e la dicitura pertinente è stata modificata da CWG issue 1301.

fornitori del compilatore di solito attuare risoluzioni difetti, quindi questo potrebbe essere considerato un bug nel GCC 4.8 che è stato fissato in 4,9.