Inizializzazione di un’unione con un costruttore non banale

Ho una struttura che creo un costruttore personalizzato per inizializzare i membri su 0. Ho visto nei vecchi compilatori che, in modalità di rilascio, senza fare un memset a 0, i valori non sono inizializzati.

Ora voglio usare questa struttura in un sindacato, ma ottenere errori perché ha un costruttore non banale.

Quindi, domanda 1. Il compilatore implementato dal compilatore predefinito garantisce che tutti i membri di una struttura verranno inizializzati con null? Il costruttore non banale esegue semplicemente un memset di tutti i membri su “0” per garantire una struttura pulita.

Domanda 2: Se un costruttore deve essere specificato sulla struttura di base, come può essere implementata un’unione per contenere quell’elemento e garantire un elemento di base 0 inizializzato?

Domanda 1: I costruttori predefiniti inizializzano i membri POD su 0 in base allo standard C ++. Vedi il testo citato qui sotto.

Domanda 2: se un costruttore deve essere specificato in una class base, allora quella class non può far parte di un sindacato.

Infine, puoi fornire un costruttore per la tua unione:

union U { A a; B b; U() { memset( this, 0, sizeof( U ) ); } }; 

Per il primo trimestre:

Da C ++ 03, 12.1 Costruttori, pg 190

Il costruttore predefinito implicitamente definito esegue l’insieme di inizializzazioni della class che verrebbe eseguito da un costruttore predefinito scritto dall’utente per quella class con un elenco di inizializzatore-mem vuoto (12.6.2) e un corpo di funzione vuoto.

Da C ++ 03, inizializzatori 8.5, pg 145

Per default-inizializzare un object di tipo T significa:

  • se T è un tipo di class non POD (clausola 9), viene chiamato il costruttore predefinito per T (e l’inizializzazione è mal formata se T non ha un costruttore predefinito accessibile);
  • se T è un tipo di matrice, ogni elemento viene inizializzato di default;
  • in caso contrario, l’object viene inizializzato a zero .

Per azzerare l’inizializzazione di un object di tipo T significa:

  • se T è un tipo scalare (3.9), l’object è impostato sul valore di 0 (zero) convertito in T;
  • se T è un tipo di class non-union, ogni membro di dati non statici e ciascun subobject di class base viene inizializzato a zero ;
  • se T è un tipo di unione, il primo membro dati con nome dell’object viene inizializzato a zero;
  • se T è un tipo di matrice, ogni elemento è inizializzato a zero;
  • se T è un tipo di riferimento, non viene eseguita alcuna inizializzazione.

Per il secondo trimestre:

Da C ++ 03, 12.1 Costruttori, pg 190

Un costruttore è banale se si tratta di un costruttore predefinito implicitamente dichiarato e se:

  • la sua class non ha funzioni virtuali (10.3) e nessuna class base virtuale (10.1), e
  • tutte le classi di base dirette della sua class hanno costruttori banali, e
  • per tutti i membri di dati non statici della sua class che sono di tipo class (o matrice di essi), ciascuna di queste classi ha un costruttore banale

Da C ++ 03, 9.5 Unions, pg 162

Un’unione può avere funzioni membro (inclusi costruttori e distruttori), ma non virtuali (10.3). Un sindacato non deve avere classi base. Un sindacato non deve essere usato come una class base. Un object di una class con un costruttore non banale (12.1), un costruttore di copia non banale (12.8), un distruttore non banale (12.4) o un non banale l’operatore di assegnazione delle copie (13.5.3, 12.8) non può essere un membro di un sindacato, né un array di tali oggetti

Le cose sono cambiate in meglio in C ++ 11.

Ora puoi farlo legalmente, come descritto da Stroustrup stesso (ho raggiunto il link dall’articolo di Wikipedia su C ++ 11 ).

L’esempio su Wikipedia è il seguente:

 #include  // Required for placement 'new'. struct Point { Point() {} Point(int x, int y): x_(x), y_(y) {} int x_, y_; }; union U { int z; double w; Point p; // Illegal in C++03; legal in C++11. U() {new(&p) Point();} // Due to the Point member, a constructor // definition is now *required*. }; 

Stroustrup entra in un piccolo dettaglio.

I membri del sindacato AFAIK non possono avere costruttori o distruttori.

Domanda 1: no, non esiste tale garanzia. Qualsiasi membro POD non incluso nell’elenco di inizializzazione del costruttore viene inizializzato automaticamente, ma è con un costruttore che si definisce e ha un elenco di inizializzazione. Se non si definisce un costruttore o si definisce un costruttore senza un elenco inizializzatore e un corpo vuoto, i membri POD non verranno inizializzati.

I membri non POD saranno sempre costruiti tramite il loro costruttore predefinito, che se sintetizzato, non inizializzerà di nuovo i membri POD. Dato che i membri del sindacato potrebbero non avere costruttori, ti verrebbe praticamente garantito che i membri POD delle strutture in un sindacato non verranno inizializzati.

Domanda 2: puoi sempre inizializzare strutture / sindacati in questo modo:

 struct foo { int a; int b; }; union bar { int a; foo f; }; bar b = { 0 }; 

Come menzionato nel commento di Greg Rogers al post di unwesen , puoi dare al tuo sindacato un costruttore (e un distruttore se lo desideri):

 struct foo { int a; int b; }; union bar { bar() { memset(this, 0, sizeof(*this)); } int a; foo f; }; 

Puoi fare qualcosa del genere?

 class Outer { public: Outer() { memset(&inner_, 0, sizeof(inner_)); } private: union Inner { int qty_; double price_; } inner_; }; 

… o forse qualcosa del genere?

 union MyUnion { int qty_; double price_; }; void someFunction() { MyUnion u = {0}; } 

Dovrai aspettare che C ++ 0x sia supportato dai compilatori per ottenere questo. Fino ad allora, mi dispiace.