2010-12-09 14 views
5

Puedo estar equivocado, pero la explicación básica que he encontrado ha sido que la unión no se puede inicializar porque no sabe a qué miembro llamar. El compilador no puede generar automáticamente un constructor para la unión.¿Por qué las uniones anónimas no pueden contener miembros con constructores/destructores no triviales?

¿Por qué el usuario no puede definir el constructor de uniones? Esto eliminaría dicho problema y permitiría la presencia de miembros sindicales que tengan un constructor/destructor no trivial.

Además, ¿por qué un miembro de la unión no puede tener constructores personalizados? La explicación anterior no representa constructores personalizados.

Actualización 1:

Ejemplo:

struct SQuaternion 
{ 
    union 
    { 
     S3DVector Axis; 
     struct 
     { 
      float X; 
      float Y; 
      float Z; 
     }; 
    }; 
    float W; 
}; 

Nota: El problema aquí parece ser que la unión es anónima. Como tal, ¿cómo se podría nombrar al constructor de la unión? Parece imposible hacerlo, simplemente porque no tiene nombre, y por ninguna otra razón. Sería una razón terrible si fuera un simple problema léxico ...

Actualización 2: Simplemente al envolver al miembro infractor en una estructura anónima envolvente, el error desaparece. Supongo que esto es lo más cercano que se puede hacer con una unión anónima. El hecho de que deja de ser un problema aún parece extraño ...

+1

funciones miembros no están permitidos en las uniones anónimas. Entonces, lo que estás buscando ni siquiera es posible, para empezar. –

+0

Eso es más o menos lo que estaba buscando o si había alguna manera alternativa de manejar esto. –

+0

Las estructuras anónimas 'tampoco están permitidas oficialmente. –

Respuesta

13

una razón más grande sería: ¿cómo sabría la unión que destructor para llamar. El lenguaje en sí no rastrea qué miembro está activo en una unión.

Parece que C++ 0x permitirá tipos no triviales en uniones, en cuyo caso se verá obligado a implementar su propio constructor (es) y destructor. (Este último es un poco claro desde el proposal, parece que el destructor de unión no llamará a ningún miembro destructores y el destructor para el derecho tendría que invocarse manualmente.)

+1

Sí, ese es precisamente mi punto. Simplemente significa que el compilador no puede suponer, parece claro que el usuario debe ser obligado a aclarar. Sin embargo, esa opción aparentemente no está disponible. El concepto de que el usuario ni siquiera está autorizado a hacerlo y está obligado a depender completamente del compilador es problemático. –

0

Este código parece funcionar bien para mí:

typedef union uAA { 
    double dVal; 
    int iVal[2]; 

    uAA() : dVal(3.22) {} 
} UAA; 

main() { 
    UAA rdata; 

    printf("Array output: %d %d \nDouble output: %lf \n", 
     rdata.iVal[0], rdata.iVal[1], rdata.dVal); 
} 
+0

Supongo que debería decir una unión sin nombre. En ese caso, tendría que llegar a la conclusión de que la razón por la que no puede hacer esto con una unión no identificada es porque no puede escribir un constructor para una unión sin nombre, no tiene ningún nombre con el que escribe el nombre del método. Eh ... –

+0

@Sion, THX, nunca había oído hablar de los sindicatos de la ONU-nammed ... extraño concepto muy extraño ... no estoy seguro de que veo su interés por el momento ... –

+0

que utiliza el término incorrecto, lo que debería han dicho unión anónima, pero el concepto es el mismo. Los miembros de un sindicato anónimo son más o menos miembros de la membresía que encierra al sindicato. –

0

Usted ha oído bien - constructores no triviales para los miembros sugieren el objeto está destinado a encapsular su contenido de datos, y las uniones eliminan esa encapsulación. Poner una unión de tipos simples en una clase es una gran manera de agregar encapsulado y asegurarse de que el contenido de la unión se use de una manera sensata y segura.

sindicatos Re: que tienen los miembros de 9,5

Una unión pueden tener funciones miembro (incluyendo los constructores y destructores), pero no (10.3) funciones virtuales. Una unión no debe tener clases base. Una unión no se utilizará como una clase base. 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 elemento no trivial. operador de asignación de copia (13.5.3, 12.8) no puede ser miembro de un sindicato, ni puede un conjunto de tales objetos

+0

Sí, pero eso es sólo otra reiteración de la regla y tengo la razón, pero por qué no hay capacidad para definir una pseudoartrosis sin nombre anónimos constructores/y el destructor de invocar el correcto constructores miembros/destructor de forma manual? ¿Existe esa capacidad? –

+0

@Sion: ¿no puede darle un nombre a la unión, definir el constructor/destructor? ¿Por qué mantener la unión anónima? Por cierto, si tiene necesidades en torno a los sindicatos y su uso limpio, debería considerar boost :: variant. –

+0

Porque una unión anónima otorga a todos sus miembros la membresía adjunta. –

2

Probablemente sería posible definir tales una cosa, pero eso abriría una lata de gusanos que casi seguro no valdría la pena:

  1. ¿Qué objeto construir?
  2. Si define un constructor de unión, ¿cómo construye un objeto frente a otro?
  3. ¿Debe definir un constructor por miembro que no sea pod?
  4. ¿Cuál es la sintaxis para asignar a un objeto diferente al asignado actualmente?
  5. ¿Permite que un objeto se interprete en el contexto de otro? Este es permitido para los sindicatos actualmente, y es un caso de uso muy común (union { char c[4]; int n; } u; u.n=1234; cout << u.c[1];).
  6. ¿Cómo sabe el compilador a qué destructor llamar?
  7. ¿Cómo sabe el compilador a qué constructor de copias llamar al copiar una unión?

(he dejado algo?)

Sospecho que sólo entró en la canasta demasiado duro.

+0

1) El que explícitamente construyes en tu constructor. 6) El que destruye explícitamente en su destructor. 7) El que llamas explícitamente en tu copia-constructor. 2) No entiendo lo que quiere decir, aclare. 3) ¿Por qué sería necesario? 4) Igual que siempre, también debería haber un operador de asignación explícito definido. 5) Si eliminaste esa capacidad, eliminarías parte del diseño inherente de una unión. –

+0

@Sion: las preguntas fueron principalmente retóricas. Abrí con la observación de que probablemente sería posible. –

+0

Es posible, aparentemente no es así con las uniones anónimas. –

1

Creo que tienes razón, los sindicatos en C++ tienen pocas características. Son prácticamente una copia directa de los sindicatos de C, lo que significa que no sirven como tipos de variantes para C++.

No hay una manera simple para que union en C++ represente un tipo de variante adecuado. Considere el siguiente código, si fuera legal:

union X { 
    int i; 
    std::string s; 
}; 

X x; 
x.s = "Hello"; 
x.i = 23; 

Ninguna cantidad de constructores o destructores de X se va a garantizar que la asignación en la última línea llama ~string23 antes de guardarla. Para que el compilador lo haga, la unión debería contener algún tipo de indicador de qué tipo se almacena. Es por eso que todo debe ser POD. No sé las razones de las diferencias entre los sindicatos con nombre y sin nombre, sin embargo, esto se aplica a ambos.

Tal C++ sindicatos podría se han definido para ser como las uniones C si todos sus miembros son POD, pero para contener esta información adicional, y llamar a los destructores correctas en el momento correcto, si algún miembro no es POD. Pero este no es el simple cambio que propusiste.

Puede un tanto laboriosa escribir un tipo de variante escribiendo una clase que tiene un valor para indicar el tipo actualmente almacenado, y luego los constructores, copiar operador de asignación, y el destructor que habría puesto en su sindicato, te fuese permitido.

Utilice una matriz de caracteres para el almacenamiento, la ubicación nueva para la construcción/asignación, y una llamada directa al destructor correcto en su destructor.

Tenga cuidado con el problema de alineación: debe asegurarse de que su almacenamiento sin procesar se alinea adecuadamente para cualquiera de los tipos que coloca en él. Una forma de hacerlo es asignarlo dinámicamente. Otra es colocar su matriz de caracteres en una unión con cualquier tipo de componente integrado que tenga el mayor requisito de alineación (si no sabe: todos ellos).

La única cosa que es diferente en el uso, de la unión que desea, es que en lugar de los miembros de datos públicos int a, float b, string c, que tendrá que proporcionar métodos de acceso que devuelven un objeto proxy (posiblemente una referencia al objeto sí mismo), que es capaz de asignar correctamente, lo que significa primero llamar al destructor para el tipo anterior. Luego puede escribir x.i() = 23 en lugar de x.i = 23.

O bien, puede utilizar Boost.Variant.

+0

Boost.Variant está más cerca de una unión. Boost.Any está más cerca de un '' void * 'de verificación de tipo' – visitor

+0

@visitor: tienes razón, editado. –

Cuestiones relacionadas