2011-09-04 10 views
18

tengo una unión que tiene este aspecto:miembro de la Unión tiene un constructor de copia no trivial

union { 
    int intValue; 
    double doubleValue; 
    std::string stringValue; 
    void *pointerValue; 
} values; 

Cuando compilo, me sale este mensaje de error (sí, lo hice #include <string>):

./Value.hh:19:19: error: union member 'stringValue' has a non-trivial copy constructor     
     std::string stringValue;                   
       ^                     
/Developer/SDKs/MacOSX10.7.sdk//usr/include/c++/4.2.1/bits/basic_string.h:434:7: note: because   
     type 'std::basic_string<char>' has a user-declared copy constructor        
     basic_string(const basic_string& __str);               
    ^

puedo compilar utilizando el siguiente comando:

$ clang++ *.cc -isysroot /Developer/SDKs/MacOSX10.7.sdk/ -shared 

¿Cómo puedo utilizar un std::string en una unión?

+0

Probablemente no quiera usar una unión. –

Respuesta

22

No puede.

Una unión combina dos piezas de funcionalidad: la capacidad de almacenar un objeto que puede ser de un número selecto de tipos, y la capacidad de convertir efectivamente (y definido por la implementación) entre esos tipos. Podría poner un número entero y ver su representación como un doble. Etcétera.

Dado que una unión debe admitir ambas de estas funciones (y por algunas otras razones, como la posibilidad de construir una), una unión impide que usted haga ciertas cosas. Es decir, no puede poner objetos "vivos" en ellos. Cualquier objeto que esté "vivo" lo suficiente como para necesitar un constructor de copia no predeterminado (entre muchas otras restricciones) no puede ser miembro de una unión.

Después de todo, un objeto de unión realmente no tiene el concepto de qué tipo de datos almacena realmente. No almacena un tipo de datos; almacena todos de ellos, al mismo tiempo. Depende de usted poder pescar el tipo correcto. Entonces, ¿cómo podría copiar razonablemente un valor de unión en otro?

Los miembros de una unión deben ser de tipo POD (plain-old-data). Y aunque C++ 11 afloja esas reglas, los objetos aún deben tener un constructor de copia predeterminado (o trivial). Y el constructor de copias de std::string no es trivial.

Lo que probablemente desee es un boost::variant. Ese es un objeto que puede almacenar una cantidad de tipos posibles, como una unión. Sin embargo, a diferencia de un sindicato, es seguro. Por lo tanto, sabe lo que realmente está en la unión; por lo tanto, puede copiarse y comportarse como un objeto normal de C++.

5

No puede poner un std::string en una unión. No está permitido por el lenguaje C++ porque no es seguro. Tenga en cuenta que la mayoría de las implementaciones std::string tienen un puntero a alguna memoria dinámica que contiene el valor de la cadena. Considere también que no hay forma de saber qué miembro de la unión está actualmente activo.

Una implementación no puede llamar al destructor de std::string, porque no sabe que el objeto std::string es el miembro activo actualmente, pero si no llama al destructor, la memoria se filtrará.

+3

Creo que en C++ 11 puede colocar tipos con constructores/destructores no triviales en una unión, pero debe llamarlos explícitamente con la sintaxis de ubicación nueva y t. ~ T(). 9.5.3-9.5.4 –

1

union no puede tener Miembro de los siguientes tipos §9.5/1:

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

Así que, o se define un puntero a std :: string como:

union { 
    int intValue; 
    double doubleValue; 
    std::string *stringValue; //pointer 
    void *pointerValue; 
} values; 

o, unión utilización impulso que se conoce como Boost.Variant

+0

¿Te importaría echarle un vistazo a mi comentario sobre la respuesta de Alok (para evitar repetirme) y dejarme saber alguna idea? –

3

De acuerdo con el estándar 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 au nión.

Por lo tanto, los miembros de una unión no pueden tener constructores, destructores, funciones de miembros virtuales o clases base. Por lo tanto, no puede usar std :: string como miembro de union.

Solución alternativa:

Puede utilizar impulso :: variante o impulso :: cualquier.

+0

Parece que necesito investigar por qué puedo poner una clase con una tarea de copia personalizada 'operator = (T const & that)' en una 'unión' muy bien ... muchas veces. ¿Se eliminó esta restricción en C++ 14, o la 'unión' está retrocediendo 'a otra asignación de copia implícita, _e.g._ 'operator = (T that)'? –

+0

^Creo que lo anterior era solo que el compilador no era lo suficientemente estricto conmigo. Ahora evito ese patrón: ninguno de mis miembros 'union' tiene cosas que no deberían. Y todos todavía funcionan bien. :) –

2

Desafortunadamente, no puede usar tipos que no sean POD (datos antiguos simples) en una unión. Una solución bastante típica y simple para esto es envolver la unión en una estructura, y mover la instancia que no es POD desde el interior de la unión a la estructura.

Por ejemplo:

struct Value { 
    union { 
     int intValue; 
     double doubleValue; 
     void *pointerValue; 
    }; 
    std::string stringValue; 
}; 

Value value; 

// Demonstration of accessing members: 

value.intValue = 0; 
value.doubleValue = 0.0; 
value.pointerValue = NULL; 
value.stringValue = "foo"; 

Usted sin embargo pagar un precio por esto - el consumo de memoria de la Value estructura será más grande que la de la unión inicial.

Cuestiones relacionadas