Domanda

Imparentato: Come inizializzare un membro non POD in Union

Lo dice la norma

Al massimo un membro dati non statico di un'unione può avere un inizializzatore di parentesi graffe o uguale.

Ma

struct Point {
    Point() {}
    Point(int x, int y): x_(x), y_(y) {}
    int x_, y_;
};

union U {
    int z;
    double w;
    Point p = Point(1,2);
};


#include <iostream>
int main () {
    U u;
    std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;
}

stampe 4196960:0 invece del previsto 1:2.

Considero questo un bug del compilatore.È così?

È stato utile?

Soluzione

C++11 [class.ctor]/5 afferma:

UN predefinito costruttore per una classe X è un costruttore di classe X che può essere chiamato senza discussione.Se non esiste un costruttore dichiarato dall'utente per class X, un costruttore che non ha parametri viene implicitamente dichiarato come predefinito (8.4).Un costruttore predefinito dichiarato implicitamente è an inline public membro della sua classe.Un costruttore predefinito predefinito per la classe X si definisce cancellato se:

  • X è una classe simile a un'unione che ha un membro variante con un costruttore predefinito non banale,
  • qualsiasi membro dati non statico con n inizializzatore di parentesi graffe o uguale è di tipo riferimento,
  • qualsiasi membro dati non variante non statico di tipo qualificato const (o relativo array) con n inizializzatore di parentesi graffe o uguale non ha un costruttore predefinito fornito dall'utente,
  • X è un'unione e tutti i suoi membri varianti sono di tipo const qualificato (o relativo array),
  • X è una classe non-union e tutti i membri di qualsiasi membro dell'unione anonima sono di tipo const-qualificato (o array di esso),
  • qualsiasi classe base diretta o virtuale o membro dati non statico con n inizializzatore di parentesi graffe o uguale, ha un tipo di classe M (o il suo array) e uno dei due M non ha un costruttore predefinito o una risoluzione dell'overload (13.3) come applicato a Mil costruttore predefinito di risulta in un'ambiguità o in una funzione eliminata o inaccessibile dal costruttore predefinito predefinito, oppure
  • qualsiasi classe base diretta o virtuale o membro dati non statico ha un tipo con un distruttore eliminato o inaccessibile dal costruttore predefinito predefinito.

Un costruttore predefinito è banale se non è fornito dall'utente e se:

  • la sua classe non ha funzioni virtuali (10.3) e nessuna classe base virtuale (10.1), e
  • nessun membro dati non statico della sua classe ha a inizializzatore di parentesi graffe o uguale, E
  • tutte le classi base dirette della sua classe hanno banali costruttori predefiniti e
  • per tutti i membri dati non statici della sua classe che sono di tipo classe (o array di essi), ciascuna di queste classi ha un banale costruttore predefinito.

Altrimenti, il costruttore predefinito è non banale.

Poiché la struttura Point nell'OP ha un costruttore predefinito non banale,

Point() {}

un costruttore predefinito predefinito per un'unione contenente un membro di tipo Point Dovrebbe essere definito eliminato secondo il primo punto:

  • X è una classe simile a un'unione che ha un membro variante con un costruttore predefinito non banale

con conseguente mal formato del programma presentato nel PO.

Tuttavia, il comitato sembra considerare questo un difetto nel caso in cui un membro di un sindacato abbia un inizializzatore di parentesi graffe o uguale, per numero 1623 del gruppo di lavoro principale:

Secondo 12.1 [class.ctor] paragrafo 5,

Un costruttore predefinito predefinito per la classe X viene definito come eliminato se:

  • X è una classe simile a un'unione che ha un membro variante con un costruttore predefinito non banale,

  • ...

  • X è un'unione e tutti i suoi membri varianti sono di tipo const qualificato (o relativo array),

  • X è una classe non-union e tutti i membri di qualsiasi membro dell'unione anonima sono di tipo const-qualificato (o array di esso),

  • ...

Poiché la presenza di un inizializzatore di membro dati non statico è l'equivalente morale di a inizializzatore mem, queste regole dovrebbero probabilmente essere modificate per non definire il costruttore generato come eliminato quando un membro dell'unione ha un inizializzatore del membro dati non statico.(Notare i riferimenti non normativi in ​​9.5 [class.union] paragrafi 2-3 e 7.1.6.1 [dcl.type.cv] paragrafo 2 che dovrebbero essere aggiornati anche se questa restrizione viene modificata.)

Sarebbe anche utile aggiungere un requisito a 9.5 [class.union] che richiede un inizializzatore di membri dati non statici o un costruttore fornito dall'utente se tutti i membri dell'unione hanno tipi qualificati const.

In una nota più generale, perché il costruttore predefinito viene definito come eliminato solo perché un membro ha un costruttore predefinito non banale?L'unione stessa non sa quale membro è quello attivo e la costruzione predefinita non inizializzerà alcun membro (assumendo che non inizializzatore di parentesi graffe o uguale).Spetta al "proprietario" dell'unione controllare la durata del membro attivo (se presente) e richiedere un costruttore fornito dall'utente forza un modello di progettazione che non ha senso.Sulla stessa linea, perché il distruttore predefinito viene definito eliminato solo perché un membro ha un distruttore non banale?Sarei d'accordo con questa restrizione se si applicasse solo quando l'unione ha anche un costruttore fornito dall'utente.

Il numero 1623 ha lo status di "redazione", indicando che il comitato ritiene che il problema sia probabilmente un difetto: altrimenti perché consentire un inizializzatore di parentesi graffe o uguale per un membro del sindacato?- ma non ha ancora dedicato il tempo per determinare la formulazione adeguata di una risoluzione.In effetti, il paragrafo è in gran parte lo stesso nell'attuale bozza C++14 N3936 ([class.ctor]/4), tranne per il fatto che l'espressione "qualsiasi classe base diretta o virtuale o membro dati non statico" è ovunque sostituita dalla più semplice "qualsiasi sottooggetto potenzialmente costruito".

Sebbene il comportamento di entrambi i compilatori non sia strettamente conforme, ritengo che Clang si comporti nello spirito dello standard.Sembrerebbe che GCC venga confuso dalla combinazione del costruttore predefinito eliminato e inizializzatore di parentesi graffe o uguale:

GCC dovrebbe probabilmente conformarsi allo standard e diagnosticare il programma come mal formato, oppure emulare il comportamento di clang e generare un costruttore appropriato dal inizializzatore di parentesi graffe o uguale.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top