Frage

Verwandt: So initialisieren Sie ein Nicht-POD-Mitglied in Union

Der Standard sagt

Höchstens ein nicht statisches Datenelement einer Union darf einen Klammer-oder-Gleich-Initialisierer haben.

Aber

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;
}

druckt 4196960:0 statt des erwarteten 1:2.

Ich halte das für einen Compiler-Fehler.Ist das so?

War es hilfreich?

Lösung

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

A Standard Konstruktor für eine Klasse X ist ein Konstruktor der Klasse X das kann ohne Argument aufgerufen werden.Wenn es keinen vom Benutzer deklarierten Konstruktor für die Klasse gibt X, wird ein Konstruktor ohne Parameter implizit als Standardwert (8.4) deklariert.Ein implizit deklarierter Standardkonstruktor ist ein inline public Mitglied seiner Klasse.Ein standardmäßiger Standardkonstruktor für die Klasse X gilt als gelöscht, wenn:

  • X ist eine gewerkschaftsähnliche Klasse, die über ein Variantenmitglied mit einem nicht trivialen Standardkonstruktor verfügt.
  • jedes nicht-statische Datenelement mit Nr Klammer-oder-Gleich-Initialisierer ist vom Referenztyp,
  • jedes nicht variante, nicht statische Datenelement vom const-qualifizierten Typ (oder Array davon) mit Nr Klammer-oder-Gleich-Initialisierer verfügt nicht über einen vom Benutzer bereitgestellten Standardkonstruktor.
  • X ist eine Union und alle ihre Variantenmitglieder sind von einem const-qualifizierten Typ (oder einem Array davon),
  • X ist eine Nicht-Gewerkschaftsklasse und alle Mitglieder eines anonymen Gewerkschaftsmitglieds sind von einem const-qualifizierten Typ (oder einem Array davon).
  • jede direkte oder virtuelle Basisklasse oder nicht statisches Datenelement mit Nr Klammer-oder-Gleich-Initialisierer, hat Klassentyp M (oder ein Array davon) und entweder M hat keinen Standardkonstruktor oder eine Überladungsauflösung (13.3), auf die angewendet wird MDer Standardkonstruktor von führt zu einer Mehrdeutigkeit oder zu einer Funktion, die gelöscht wird oder über den Standardkonstruktor nicht zugänglich ist, oder
  • Jede direkte oder virtuelle Basisklasse oder jedes nicht-statische Datenelement hat einen Typ mit einem Destruktor, der gelöscht ist oder über den standardmäßigen Standardkonstruktor nicht zugänglich ist.

Ein Standardkonstruktor ist trivial, wenn er nicht vom Benutzer bereitgestellt wird und wenn:

  • seine Klasse hat keine virtuellen Funktionen (10.3) und keine virtuellen Basisklassen (10.1) und
  • Kein nicht statisches Datenelement seiner Klasse hat ein Klammer-oder-Gleich-Initialisierer, Und
  • Alle direkten Basisklassen seiner Klasse verfügen über triviale Standardkonstruktoren und
  • Für alle nicht statischen Datenelemente ihrer Klasse, die vom Klassentyp (oder einem Array davon) sind, verfügt jede dieser Klassen über einen trivialen Standardkonstruktor.

Andernfalls ist der Standardkonstruktor nicht trivial.

Da die Struktur Point im OP gibt es einen nicht trivialen Standardkonstruktor,

Point() {}

ein standardmäßiger Standardkonstruktor für eine Union, die ein Mitglied des Typs enthält Point sollen gemäß dem ersten Aufzählungspunkt als gelöscht definiert werden:

  • X ist eine gewerkschaftsähnliche Klasse, die über ein Variantenmitglied mit einem nicht trivialen Standardkonstruktor verfügt

Dies führte dazu, dass das im OP vorgestellte Programm schlecht formuliert war.

Das Komitee scheint dies jedoch für einen Mangel zu halten, wenn ein Mitglied einer Gewerkschaft einen solchen hat Klammer-oder-Gleich-Initialisierer, pro Kernarbeitsgruppenausgabe 1623:

Gemäß 12.1 [class.ctor] Absatz 5,

Ein standardmäßiger Standardkonstruktor für Klasse X wird als gelöscht definiert, wenn:

  • X ist eine gewerkschaftsähnliche Klasse, die über ein Variantenmitglied mit einem nicht trivialen Standardkonstruktor verfügt.

  • ...

  • X ist eine Union und alle ihre Variantenmitglieder sind von einem const-qualifizierten Typ (oder einem Array davon),

  • X ist eine Nicht-Gewerkschaftsklasse und alle Mitglieder eines anonymen Gewerkschaftsmitglieds sind von einem const-qualifizierten Typ (oder einem Array davon).

  • ...

Weil das Vorhandensein eines nicht statischen Datenelementinitialisierers das moralische Äquivalent von a ist mem-initializer, sollten diese Regeln wahrscheinlich geändert werden, um den generierten Konstruktor nicht als gelöscht zu definieren, wenn ein Gewerkschaftsmitglied über einen nicht statischen Datenelementinitialisierer verfügt.(Beachten Sie die nicht normativen Verweise in 9.5 [class.union] Absätze 2-3 und 7.1.6.1 [dcl.type.cv] Absatz 2, die ebenfalls aktualisiert werden müssten, wenn diese Einschränkung geändert wird.)

Es wäre auch hilfreich, eine Anforderung zu 9.5 [class.union] hinzuzufügen, die entweder einen nicht statischen Datenmember-Initialisierer oder einen vom Benutzer bereitgestellten Konstruktor erfordert, wenn alle Mitglieder der Union über const-qualifizierte Typen verfügen.

Allgemeiner gesagt: Warum wird der Standardkonstruktor als gelöscht definiert, nur weil ein Mitglied einen nicht trivialen Standardkonstruktor hat?Die Union selbst weiß nicht, welches Mitglied das aktive ist, und die Standardkonstruktion initialisiert keine Mitglieder (vorausgesetzt, nein). Klammer-oder-Gleich-Initialisierer).Es ist Sache des „Eigentümers“ der Union, die Lebensdauer des aktiven Mitglieds (falls vorhanden) zu steuern, und die Anforderung eines vom Benutzer bereitgestellten Konstruktors erzwingt ein Entwurfsmuster, das keinen Sinn ergibt.Warum wird in ähnlicher Weise der Standarddestruktor als gelöscht definiert, nur weil ein Mitglied einen nicht trivialen Destruktor hat?Ich würde dieser Einschränkung zustimmen, wenn sie nur gelten würde, wenn die Union auch über einen vom Benutzer bereitgestellten Konstruktor verfügt.

Problem 1623 hat den Status „Entwurf“, was darauf hinweist, dass das Komitee davon ausgeht, dass es sich bei dem Problem wahrscheinlich um einen Mangel handelt – warum sollte man sonst a zulassen? Klammer-oder-Gleich-Initialisierer für ein Gewerkschaftsmitglied?- hat sich aber noch nicht die Zeit genommen, den richtigen Wortlaut für eine Resolution festzulegen.Tatsächlich ist der Absatz im aktuellen C++14-Entwurf N3936 ([class.ctor]/4) weitgehend identisch, mit der Ausnahme, dass die Formulierung „jede direkte oder virtuelle Basisklasse oder nicht statisches Datenelement“ überall durch ersetzt wird einfacher: „jedes potenziell konstruierte Unterobjekt“.

Obwohl das Verhalten beider Compiler nicht strikt konform ist, würde ich davon ausgehen, dass sich Clang im Sinne des Standards verhält.Es scheint, dass GCC durch die Kombination aus gelöschtem Standardkonstruktor und verwirrt wird Klammer-oder-Gleich-Initialisierer:

GCC sollte wahrscheinlich entweder dem Standard entsprechen und das Programm als fehlerhaft diagnostizieren oder das Verhalten von Clang emulieren und daraus einen geeigneten Konstruktor generieren Klammer-oder-Gleich-Initialisierer.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top