¿Por qué el comstackdor no permite std :: string dentro de la unión?

Quiero usar una cuerda dentro de Union. si escribo como abajo

union U { int i; float f; string s; }; 

El comstackdor da error diciendo que U :: S tiene un constructor de copia.

Leí alguna otra publicación para encontrar formas alternativas de resolver este problema. Pero quiero saber por qué el comstackdor no permite esto en primer lugar?

EDITAR: @KennyTM: en cualquier unión, si el miembro se inicializa, otros tendrán valores basura, si ninguno se inicializa, todos tendrán valores basura. Creo que la unión etiquetada solo proporciona cierta comodidad para acceder a los valores válidos de Union. Tu pregunta: ¿cómo escribes tú o el comstackdor un constructor de copias para la unión anterior sin información adicional? sizeof (cadena) da 4 bytes. En base a esto, el comstackdor puede comparar el tamaño de otros miembros y asignar la asignación más grande (4 bytes en nuestro ejemplo). La longitud interna de la cuerda no importa porque se almacenará en una ubicación separada. Deje que la cuerda sea de cualquier longitud. Todo lo que la Unión tiene que saber es invocar el constructor de copia de clase de cadena con el parámetro de cadena. En cualquier forma en que el comstackdor encuentre que se debe invocar el constructor de copia en el caso normal, se seguirá un método similar incluso cuando la cadena esté dentro de Unión. Así que estoy pensando que el comstackdor podría hacer, asignar 4 bytes. Entonces, si se asigna una cadena a s, la clase de cadena se encargará de la asignación y copia de esa cadena utilizando su propio asignador. Entonces tampoco hay posibilidad de corrupción en la memoria.

¿La cadena no existe en el momento del desarrollo de la Unión en el comstackdor? Entonces la respuesta aún no está clara para mí. Soy un nuevo socio en este sitio, si algo está mal, por favor discúlpeme.

Piénsalo. ¿Cómo sabe el comstackdor qué tipo hay en la unión?

No es así La operación fundamental de una unión es esencialmente un reparto de bits. Las operaciones con los valores contenidos en las uniones solo son seguras cuando cada tipo se puede llenar con basura. std::string no puede, porque daría lugar a daños en la memoria. Utilice boost::variant o boost::any .

Porque tener una clase con un constructor no trivial (copy /) en una unión no tiene sentido. Supongamos que tenemos

 union U { string x; vector y; }; U u; // <-- 

Si U fuera una estructura, ux y uy se inicializarían en una cadena vacía y un vector vacío, respectivamente. Pero los miembros de un sindicato comparten la misma dirección. Por lo tanto, si ux se inicializa, uy contendrá datos no válidos, al igual que el reverso. Si ambos no se inicializan, entonces no pueden ser utilizados. En cualquier caso, tener estos datos en una unión no se puede manejar fácilmente, por lo que C ++ 98 elige denegar esto: (§9.5 / 1):

Un objeto de una clase con un constructor no trivial (12.1), un constructor de copia no trivial (12.8), un destructor no trivial (12.4) o un operador de asignación de copia no trivial (13.5.3, 12.8) no pueden ser un miembro de una unión, ni tampoco una variedad de tales objetos.

En C ++ 0x esta regla se ha relajado (§9.5 / 2):

Como máximo, un miembro de datos no estáticos de una unión puede tener un inicializador de llaves o igual . [ Nota: si algún miembro de una unión no estático tiene un constructor predeterminado no trivial (12.1), copia el constructor (12.8), mueve el constructor (12.8), copia el operador de asignación (12.8), mueve el operador de asignación (12.8), o destructor (12.4), la función miembro correspondiente de la unión debe ser proporcionada por el usuario o se eliminará implícitamente (8.4.3) para la unión. - nota final ]

pero aún no es posible crear (corregir) con / destructores para la unión, por ejemplo, ¿cómo usted o el comstackdor escriben un constructor de copia para la unión anterior sin información adicional? Para asegurarse de qué miembro de la unión está activo, necesita una unión etiquetada , y debe manejar la construcción y destrucción manualmente, por ejemplo

 struct TU { int type; union { int i; float f; std::string s; } u; TU(const TU& tu) : type(tu.type) { switch (tu.type) { case TU_STRING: new(&u.s)(tu.us); break; case TU_INT: ui = tu.ui; break; case TU_FLOAT: uf = tu.uf; break; } } ~TU() { if (tu.type == TU_STRING) us~string(); } ... }; 

Pero, como ha mencionado @DeadMG , esto ya está implementado como boost::variant o boost::any .

En C ++ 98/03, los miembros de una unión no pueden tener constructores, destructores, funciones de miembros virtuales o clases base.

Entonces, básicamente, solo puedes usar tipos de datos incorporados, o PODs

Tenga en cuenta que está cambiando en C ++ 0x: uniones sin restricciones

 union { int z; double w; string s; // Illegal in C++98, legal in C++0x. }; 

De la especificación de C ++ §9.5.1:

Un objeto de una clase con un constructor no trivial, un constructor de copia no trivial, un destructor no trivial o un operador de asignación de copia no trivial no puede ser miembro de una unión.

El motivo de esta regla es que el comstackdor nunca sabrá cuál de los destructores / constructores llama, ya que nunca sabe realmente cuál de los posibles objetos se encuentra dentro de la unión.

La basura se introduce si

  1. asignar una cadena
  2. luego asigna un int o float
  3. a continuación, una cadena de nuevo

cadena maneja la memoria en otro lugar. Esta información probablemente sea un puntero. Este puntero está guardado cuando se asigna el int. Asignar una nueva cadena debería destruir la cadena anterior, lo cual no es posible.

El segundo paso debe destruir la cadena, pero no lo sabe, si ha habido una cadena.

Obviamente, han encontrado una solución para este problema mientras tanto.