lenguaje - que es una union c++
Unión anónima y estructura (3)
Creo que las otras respuestas no entendieron el punto de la pregunta:
Deseo acceder a los miembros de la misma manera que lo haría con esto.
En otras palabras, la pregunta es realmente "¿cómo defino un tipo vec
de una manera que cumpla con los estándares, de modo que dado un objeto u
de ese tipo, ux
, ur
y u.elements[0]
refieran a la misma cosa? "
Bueno, si insistes en esa sintaxis ... entonces la respuesta obvia es: referencias.
Asi que:
template <typename some_type>
struct vec
{
vec() = default;
vec(const vec& other) : elements{ other.elements[0], other.elements[1], other.elements[2] } {}
vec & operator=(const vec &other) {
elements[0] = other.elements[0];
elements[1] = other.elements[1];
elements[2] = other.elements[2];
return *this;
}
some_type elements[3];
some_type &x = elements[0], &y = elements[1], &z = elements[2];
some_type &r = elements[0], &g = elements[1], &b = elements[2];
};
El primer problema con este enfoque es que necesita espacio adicional para 6 miembros de referencia, lo que es bastante caro para una estructura tan pequeña.
El segundo problema con este enfoque es que dado const vec<double> v;
, vx
sigue siendo de tipo double &
, por lo que podría escribir vx = 20;
y haga que se compile sin advertencia ni error, solo para obtener un comportamiento indefinido. Bastante mal.
Entonces, en la alternativa, podría considerar el uso de funciones de acceso:
template <typename some_type>
struct vec
{
some_type elements[3];
some_type &x() { return elements[0]; }
const some_type &x() const { return elements[0]; }
some_type &y() { return elements[1]; }
const some_type &y() const { return elements[1]; }
some_type &z() { return elements[2]; }
const some_type &z() const { return elements[2]; }
some_type &r() { return elements[0]; }
const some_type &r() const { return elements[0]; }
some_type &g() { return elements[1]; }
const some_type &g() const { return elements[1]; }
some_type &b() { return elements[2]; }
const some_type &b() const { return elements[2]; }
};
Tendría que escribir ux()
etc. en lugar de ux
, pero el ahorro de espacio es considerable, también puede confiar en las funciones miembro especiales generadas por el compilador, se puede copiar de forma trivial si some_type es (lo que permite algunas optimizaciones), es un agregado y así puede usar la sintaxis de inicialización agregada, y también es const-correct.
Demo Tenga en cuenta que sizeof(vec<double>)
es 72 para la primera versión y solo 24 para la segunda.
Esta pregunta ya tiene una respuesta aquí:
- ¿Por qué C ++ no permite estructuras anónimas? 6 respuestas
¿Cómo harías para hacer esto en el estándar C ++ 11/14? Porque si no me equivoco, este no es un código compatible con las estructuras anónimas.
Deseo acceder a los miembros de la misma manera que lo haría con esto.
template <typename some_type>
struct vec
{
union {
struct { some_type x, y, z; };
struct { some_type r, g, b; };
some_type elements[3];
};
};
Los sindicatos anónimos están permitidos en C ++ 11/14. Vea el ejemplo de su uso en las Preguntas frecuentes sobre C ++ 11 de Bjarne Stroustrup
Con respecto a las estructuras anónimas, vea ¿Por qué C ++ 11 no admite las estructuras anónimas, mientras que C11 sí lo hace? y ¿Por qué C ++ no permite estructuras y uniones anónimas?
Aunque la mayoría de los compiladores admiten estructuras anónimas, si desea que su código sea compatible con el estándar, tiene que escribir algo como esto:
template <typename some_type>
struct vec
{
union {
struct { some_type x, y, z; } s1;
struct { some_type r, g, b; } s2;
some_type elements[3];
};
};
Sí, ni C ++ 11 ni C ++ 14 permiten estructuras anónimas. Esta respuesta contiene algún razonamiento de por qué este es el caso. Debe nombrar las estructuras, y tampoco se pueden definir dentro de la unión anónima.
§9.5 / 5 [class.union]
...
La especificación de miembro de una unión anónima solo definirá miembros de datos no estáticos. [ Nota: los tipos anidados, las uniones anónimas y las funciones no se pueden declarar dentro de una unión anónima. "Nota final "
Así que mueva las definiciones de la estructura fuera de la unión.
template <typename some_type>
struct vec
{
struct xyz { some_type x, y, z; };
struct rgb { some_type r, g, b; };
union {
xyz a;
rgb b;
some_type elements[3];
};
};
Ahora, necesitamos que some_type
sea de diseño estándar porque eso hace que todos los miembros del diseño de la unión anónima sean compatibles . Aquí están los requisitos para un tipo de diseño estándar. Estos se describen en la sección §9 / 7 de la norma.
Luego, de §9.2 [class.mem]
16 Dos tipos de estructura de diseño estándar (Cláusula 9) son compatibles con el diseño si tienen el mismo número de miembros de datos no estáticos y los miembros de datos no estáticos correspondientes (en orden de declaración) tienen tipos compatibles con el diseño (3.9).
18 Si una unión de diseño estándar contiene dos o más estructuras de diseño estándar que comparten una secuencia inicial común, y si el objeto de unión de diseño estándar contiene actualmente una de estas estructuras de diseño estándar, está permitido inspeccionar la parte inicial común de cualquiera de ellos. Dos estructuras de diseño estándar comparten una secuencia inicial común si los miembros correspondientes tienen tipos compatibles con el diseño y ninguno de los dos es un campo de bits o ambos son campos de bits con el mismo ancho para una secuencia de uno o más miembros iniciales.
Y para el miembro de la matriz, de §3.9 / 9 [basic.types]
...
Los tipos escalares, los tipos de clase de diseño estándar (Cláusula 9), las matrices de estos tipos y las versiones calificadas para CV de estos tipos (3.9.3) se denominan colectivamente tipos de diseño estándar .
Para asegurarse de que some_type
es diseño estándar, agregue lo siguiente dentro de la definición de vec
static_assert(std::is_standard_layout<some_type>::value, "not standard layout");
std::is_standard_layout
se define en el encabezado type_traits
. Ahora, los 3 miembros de su sindicato son de diseño estándar, las dos estructuras y la matriz son compatibles con el diseño, por lo que los 3 miembros de la unión comparten una secuencia inicial común, que le permite escribir y luego inspeccionar (leer) a todos los miembros que pertenecen al común secuencia inicial (toda la cosa en tu caso).