Question

En rapport: Comment initialiser un membre non-POD dans Union

La norme dit

Au plus une donnée membre non statique d'une union peut avoir un initialiseur accolade ou égal.

Mais

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

impressions 4196960:0 au lieu de ce qui était attendu 1:2.

Je considère cela comme un bug du compilateur.Est-ce ainsi?

Était-ce utile?

La solution

C++11 [class.ctor]/5 déclare :

UN défaut constructeur pour une classe X est un constructeur de classe X qui peut être appelé sans argument.S'il n'y a pas de constructeur déclaré par l'utilisateur pour la classe X, un constructeur n'ayant aucun paramètre est implicitement déclaré comme valeur par défaut (8.4).Un constructeur par défaut implicitement déclaré est un inline public membre de sa classe.Un constructeur par défaut pour la classe X est défini comme supprimé si :

  • X est une classe de type union qui a un membre variante avec un constructeur par défaut non trivial,
  • tout membre de données non statique sans accolade ou égal-initialiseur est de type référence,
  • tout membre de données non statique non variant de type const-qualifié (ou tableau de celui-ci) sans accolade ou égal-initialiseur n'a pas de constructeur par défaut fourni par l'utilisateur,
  • X est une union et tous ses membres variantes sont de type const-qualifié (ou un tableau de ceux-ci),
  • X est une classe non syndiquée et tous les membres de tout membre anonyme du syndicat sont de type const-qualifié (ou un tableau de ceux-ci),
  • toute classe de base directe ou virtuelle, ou membre de données non statique sans accolade ou égal-initialiseur, a un type de classe M (ou un tableau de ceux-ci) et soit M n'a pas de constructeur par défaut ni de résolution de surcharge (13.3) tel qu'appliqué à MLe constructeur par défaut de entraîne une ambiguïté ou une fonction supprimée ou inaccessible du constructeur par défaut, ou
  • toute classe de base directe ou virtuelle ou membre de données non statique a un type avec un destructeur qui est supprimé ou inaccessible du constructeur par défaut par défaut.

Un constructeur par défaut est trivial s'il n'est pas fourni par l'utilisateur et si :

  • sa classe n'a pas de fonctions virtuelles (10.3) ni de classes de base virtuelles (10.1), et
  • aucun membre de données non statique de sa classe n'a de accolade ou égal-initialiseur, et
  • toutes les classes de base directes de sa classe ont des constructeurs par défaut triviaux, et
  • pour toutes les données membres non statiques de sa classe qui sont de type classe (ou tableau de celles-ci), chacune de ces classes a un constructeur par défaut trivial.

Sinon, le constructeur par défaut est non trivial.

Depuis la structure Point dans l'OP a un constructeur par défaut non trivial,

Point() {}

un constructeur par défaut pour une union contenant un membre de type Point devrait être défini comme supprimé selon le premier point :

  • X est une classe de type union qui a un membre variante avec un constructeur par défaut non trivial

ce qui fait que le programme présenté dans le PO est mal formé.

Cependant, le comité semble considérer qu'il s'agit d'un défaut dans le cas où un membre d'un syndicat possède un accolade ou égal-initialiseur, par groupe de travail principal, question 1623:

Selon 12.1 [class.ctor] paragraphe 5,

Un constructeur par défaut pour la classe X est défini comme supprimé si :

  • X est une classe de type union qui a un membre variante avec un constructeur par défaut non trivial,

  • ...

  • X est une union et tous ses membres variantes sont de type const-qualifié (ou un tableau de ceux-ci),

  • X est une classe non syndiquée et tous les membres de tout membre anonyme du syndicat sont de type const-qualifié (ou un tableau de ceux-ci),

  • ...

Parce que la présence d'un initialiseur de données membre non statique est l'équivalent moral d'un initialiseur de mémoire, ces règles devraient probablement être modifiées pour ne pas définir le constructeur généré comme supprimé lorsqu'un membre de l'union a un initialiseur de membre de données non statique.(Notez les références non normatives dans les paragraphes 2-3 de 9.5 [class.union] et le paragraphe 2 de 7.1.6.1 [dcl.type.cv] qui devraient également être mis à jour si cette restriction est modifiée.)

Il serait également utile d'ajouter une exigence à 9.5 [class.union] exigeant soit un initialiseur de données membre non statique, soit un constructeur fourni par l'utilisateur si tous les membres de l'union ont des types const-qualifiés.

D'une manière plus générale, pourquoi le constructeur par défaut est-il défini comme supprimé simplement parce qu'un membre a un constructeur par défaut non trivial ?L'union elle-même ne sait pas quel membre est le membre actif, et la construction par défaut n'initialisera aucun membre (en supposant qu'il n'y ait aucun membre). accolade ou égal-initialiseur).Il appartient au « propriétaire » de l'union de contrôler la durée de vie du membre actif (le cas échéant), et exiger un constructeur fourni par l'utilisateur impose un modèle de conception qui n'a pas de sens.Dans le même ordre d'idées, pourquoi le destructeur par défaut est-il défini comme supprimé simplement parce qu'un membre a un destructeur non trivial ?Je serais d'accord avec cette restriction si elle ne s'appliquait que lorsque le syndicat dispose également d'un constructeur fourni par l'utilisateur.

Le numéro 1623 a le statut « en rédaction », ce qui indique que le comité estime que le problème est probablement un défaut - sinon, pourquoi autoriser un accolade ou égal-initialiseur pour un syndiqué ?- mais n'a pas encore pris le temps de déterminer la formulation appropriée d'une résolution.En effet, le paragraphe est en grande partie le même dans le projet actuel C++14 N3936 ([class.ctor]/4), sauf que la formulation « toute classe de base directe ou virtuelle ou membre de données non statique » est partout remplacée par le plus simple "tout sous-objet potentiellement construit".

Bien que le comportement des deux compilateurs ne soit pas strictement conforme, je considérerais que Clang se comporte dans l'esprit du standard.Il semblerait que GCC soit confus par la combinaison du constructeur par défaut supprimé et accolade ou égal-initialiseur:

GCC devrait probablement soit se conformer au standard et diagnostiquer le programme comme étant mal formé, soit émuler le comportement de clang et générer un constructeur approprié à partir du accolade ou égal-initialiseur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top