Initialiser une union avec un constructeur non sortingvial

J’ai une structure que je crée un constructeur personnalisé pour initialiser les membres à 0. J’ai vu dans les anciens compilateurs qu’en mode de publication, sans faire un memset à 0, les valeurs ne sont pas initialisées.

Je veux maintenant utiliser cette structure dans une union, mais obtenir des erreurs car elle a un constructeur non sortingvial.

Donc, la question 1. Le constructeur implémenté par le compilateur par défaut garantit-il que tous les membres d’une structure seront initialisés par une valeur nulle? Le constructeur non sortingvial ne fait qu’un «memset» de tous les membres pour «0» afin de garantir une structure propre.

Question 2: Si un constructeur doit être spécifié sur la structure de base, comment une union peut-elle être implémentée pour contenir cet élément et assurer un élément de base 0 initialisé?

Question 1: Les constructeurs par défaut initialisent les membres POD à 0 selon le standard C ++. Voir le texte cité ci-dessous.

Question 2: Si un constructeur doit être spécifié dans une classe de base, cette classe ne peut pas faire partie d’une union.

Enfin, vous pouvez fournir un constructeur pour votre union:

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

Pour Q1:

À partir de C ++ 03, 12.1 Constructeurs, page 190

Le constructeur par défaut défini implicitement exécute l’ensemble des initialisations de la classe qui serait effectuée par un constructeur par défaut écrit par l’utilisateur pour cette classe avec une mem-initializer-list vide (12.6.2) et un corps de fonction vide.

A partir de C ++ 03, 8.5 Initialiseurs, page 145

Initialiser par défaut un object de type T signifie:

  • si T est un type de classe non-POD (clause 9), le constructeur par défaut de T est appelé (et l’initialisation est mal formée si T n’a pas de constructeur par défaut accessible);
  • si T est un type tableau, chaque élément est initialisé par défaut;
  • sinon, l’object est initialisé à zéro .

Initialiser à zéro un object de type T signifie:

  • Si T est un type scalaire (3.9), l’object est défini sur la valeur 0 (zéro) convertie en T;
  • si T est un type de classe non-union, chaque membre de données non statique et chaque sous-object de classe de base sont initialisés à zéro ;
  • si T est un type d’union, le premier membre de données nommé de l’object est initialisé à zéro;
  • si T est un type tableau, chaque élément est initialisé à zéro;
  • si T est un type de référence, aucune initialisation n’est effectuée.

Pour Q2:

À partir de C ++ 03, 12.1 Constructeurs, page 190

Un constructeur est sortingvial s’il s’agit d’un constructeur implicite déclaré par défaut et si:

  • sa classe n’a pas de fonctions virtuelles (10.3) et aucune classe de base virtuelle (10.1), et
  • toutes les classes de base directes de sa classe ont des constructeurs sortingviaux, et
  • pour tous les membres de données non statiques de sa classe qui sont de type classe (ou leur tableau), chacune de ces classes a un constructeur sortingvial

De C ++ 03, 9.5 Unions, p. 162

Une union peut avoir des fonctions membres (y compris des constructeurs et des destructeurs), mais pas des fonctions virtuelles (10.3). Une union ne doit pas avoir de classes de base. Une union ne doit pas être utilisée comme classe de base. Un object d’une classe avec un constructeur non sortingvial (12.1), un constructeur de copie non sortingvial (12.8), un destructeur non sortingvial (12.4), ou un non-sortingvial l’opérateur d’atsortingbution de copie (13.5.3, 12.8) ne peut pas être membre d’une union, ni un tableau de ces objects

Les choses ont changé pour le mieux en C ++ 11.

Vous pouvez maintenant légalement le faire, comme décrit par Stroustrup lui-même (j’ai atteint ce lien depuis l’article de Wikipedia sur C ++ 11 ).

L’exemple sur Wikipedia est le suivant:

 #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 va plus loin dans les détails.

Les membres du syndicat AFAIK peuvent ne pas avoir de constructeurs ou de destructeurs.

Question 1: non, il n’y a pas de telle garantie. Tout membre de POD ne figurant pas dans la liste d’initialisation du constructeur est initialisé par défaut, mais avec un constructeur que vous définissez et possède une liste d’initialisation. Si vous ne définissez pas de constructeur ou si vous définissez un constructeur sans liste d’initialisation ni corps vide, les membres POD ne seront pas initialisés.

Les membres non-POD seront toujours construits via leur constructeur par défaut qui, une fois synthétisé, n’initialiserait pas à nouveau les membres POD. Étant donné que les membres de l’union n’ont peut-être pas de constructeurs, vous êtes pratiquement certain que les membres POD des structures d’union ne seront pas initialisés.

Question 2: vous pouvez toujours initialiser des structures / unions comme ceci:

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

Comme mentionné dans le commentaire de Greg Rogers de supprimer le message, vous pouvez donner à votre syndicat un constructeur (et un destructeur si vous le souhaitez):

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

Pouvez-vous faire quelque chose comme ça?

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

… ou peut-être quelque chose comme ça?

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

Vous devrez attendre que C ++ 0x soit pris en charge par les compilateurs pour l’obtenir. Jusque là, désolé.