Pregunta

Relacionado: Cómo inicializar un miembro que no es POD en Union

La norma dice

Como máximo, un miembro de datos no estáticos de una unión puede tener un inicializador de llave o igual.

Pero

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

huellas dactilares 4196960:0 en lugar de lo esperado 1:2.

Considero que esto es un error del compilador.¿Es eso así?

¿Fue útil?

Solución

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

A por defecto constructor de una clase X es un constructor de clase X que se puede llamar sin argumento.Si no hay ningún constructor declarado por el usuario para la clase X, un constructor que no tiene parámetros se declara implícitamente como predeterminado (8.4).Un constructor predeterminado implícitamente declarado es un inline public miembro de su clase.Un constructor predeterminado predeterminado para la clase. X se define como eliminado si:

  • X es una clase tipo unión que tiene un miembro variante con un constructor predeterminado no trivial,
  • cualquier miembro de datos no estático sin inicializador-llave-o-igual es de tipo referencia,
  • cualquier miembro de datos no estáticos y no variantes de tipo calificado constante (o una matriz del mismo) sin inicializador-llave-o-igual no tiene un constructor predeterminado proporcionado por el usuario,
  • X es una unión y todos sus miembros variantes son de tipo calificado constante (o una matriz de los mismos),
  • X es una clase no sindicalizada y todos los miembros de cualquier miembro sindical anónimo son de tipo calificado constantemente (o una variedad de ellos),
  • cualquier clase base directa o virtual, o miembro de datos no estático sin inicializador-llave-o-igual, tiene tipo de clase M (o conjunto de los mismos) y ya sea M no tiene constructor predeterminado ni resolución de sobrecarga (13.3) como se aplica a MEl constructor predeterminado de resulta en una ambigüedad o en una función que se elimina o es inaccesible desde el constructor predeterminado predeterminado, o
  • cualquier clase base directa o virtual o miembro de datos no estáticos tiene un tipo con un destructor que se elimina o es inaccesible desde el constructor predeterminado predeterminado.

Un constructor predeterminado es trivial si no lo proporciona el usuario y si:

  • su clase no tiene funciones virtuales (10.3) ni clases base virtuales (10.1), y
  • ningún miembro de datos no estáticos de su clase tiene un inicializador-llave-o-igual, y
  • todas las clases base directas de su clase tienen constructores predeterminados triviales, y
  • para todos los miembros de datos no estáticos de su clase que son de tipo de clase (o matriz de la misma), cada clase tiene un constructor predeterminado trivial.

De lo contrario, el constructor predeterminado es no trivial.

Desde la estructura Point en el OP tiene un constructor predeterminado no trivial,

Point() {}

un constructor predeterminado predeterminado para una unión que contiene un miembro de tipo Point debería definirse como eliminado según la primera viñeta:

  • X es una clase tipo unión que tiene un miembro variante con un constructor predeterminado no trivial

lo que dio lugar a que el programa presentado en el PO estuviera mal formulado.

Sin embargo, el comité parece considerar que esto es un defecto en el caso de que un miembro de un sindicato tenga una inicializador-llave-o-igual, por número 1623 del grupo de trabajo central:

Según 12.1 [class.ctor] párrafo 5,

Un constructor predeterminado predeterminado para la clase X se define como eliminado si:

  • X es una clase tipo unión que tiene un miembro variante con un constructor predeterminado no trivial,

  • ...

  • X es una unión y todos sus miembros variantes son de tipo calificado constante (o una matriz de los mismos),

  • X es una clase no sindicalizada y todos los miembros de cualquier miembro sindical anónimo son de tipo calificado constantemente (o una variedad de ellos),

  • ...

Debido a que la presencia de un inicializador de miembro de datos no estático es el equivalente moral de un inicializador-mem, estas reglas probablemente deberían modificarse para no definir el constructor generado como eliminado cuando un miembro de la unión tiene un inicializador de miembro de datos no estático.(Tenga en cuenta las referencias no normativas en 9.5 [class.union] párrafos 2-3 y 7.1.6.1 [dcl.type.cv] párrafo 2 que también deberían actualizarse si se cambia esta restricción).

También sería útil agregar un requisito a 9.5 [class.union] que requiera un inicializador de miembro de datos no estático o un constructor proporcionado por el usuario si todos los miembros de la unión tienen tipos calificados const.

En una nota más general, ¿por qué el constructor predeterminado se define como eliminado solo porque un miembro tiene un constructor predeterminado no trivial?El sindicato en sí no sabe qué miembro es el activo y la construcción predeterminada no inicializará ningún miembro (suponiendo que no inicializador-llave-o-igual).Depende del “propietario” de la unión controlar la vida útil del miembro activo (si lo hay), y requerir un constructor proporcionado por el usuario es forzar un patrón de diseño que no tiene sentido.En la misma línea, ¿por qué el destructor predeterminado se define como eliminado solo porque un miembro tiene un destructor no trivial?Estaría de acuerdo con esta restricción si solo se aplicara cuando la unión también tiene un constructor proporcionado por el usuario.

La edición 1623 tiene el estado "redacción", lo que indica que el comité cree que la publicación probablemente sea un defecto: ¿por qué si no permitir una inicializador-llave-o-igual para un miembro del sindicato?- pero aún no ha dedicado tiempo a determinar la redacción adecuada de una resolución.De hecho, el párrafo es en gran medida el mismo en el actual borrador N3936 de C++14 ([class.ctor]/4), excepto que la expresión "cualquier clase base directa o virtual o miembro de datos no estático" se reemplaza en todas partes por el más simple, "cualquier subobjeto potencialmente construido".

Aunque el comportamiento de ambos compiladores no es estrictamente conforme, consideraría que Clang se comporta según el espíritu del estándar.Parecería que GCC se confunde por la combinación del constructor predeterminado eliminado y inicializador-llave-o-igual:

GCC probablemente debería ajustarse al estándar y diagnosticar que el programa está mal formado, o emular el comportamiento de clang y generar un constructor adecuado a partir del inicializador-llave-o-igual.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top