c++ - rusia - goles de españa vs marruecos
¿Un tipo fantasma tiene la misma alineación que el original? (3)
Considere la siguiente estructura que contiene algunos valores de entorno:
struct environment_values {
uint16_t humidity;
uint16_t temperature;
uint16_t charging;
};
Me gustaría agregar algo de información adicional a esos valores con un tipo fantasma * y hacer que sus tipos sean distintos al mismo tiempo:
template <typename T, typename P>
struct Tagged {
T value;
};
// Actual implementation will contain some more features
struct Celsius{};
struct Power{};
struct Percent{};
struct Environment {
Tagged<uint16_t,Percent> humidity;
Tagged<uint16_t,Celsius> temperature;
Tagged<uint16_t,Power> charging;
};
¿El diseño de memoria de Environment
lo mismo que environment_values
? ¿Esto también se aplica a los diseños de tipos mixtos, por ejemplo:
struct foo {
uint16_t value1;
uint8_t value2;
uint64_t value3;
}
struct Foo {
Tagged<uint16_t, Foo> Value1;
Tagged<uint8_t , Bar> Value2;
Tagged<uint64_t, Quux> Value3;
}
Para todos los tipos que he intentado hasta ahora, las siguientes afirmaciones sostuvieron:
template <typename T, typename P = int>
constexpr void check() {
static_assert(alignof(T) == alignof(Tagged<T,P>), "alignment differs");
static_assert(sizeof(T) == sizeof(Tagged<T,P>), "size differs");
}
// check<uint16_t>(), check<uint32_t>(), check<char>() …
Dado que el tamaño de las variantes etiquetadas y sin etiquetar también es el mismo, supongo que la respuesta debería ser sí, pero me gustaría tener algo de certeza.
* No tengo idea de cómo se llaman esos valores etiquetados en C ++. "Typedefs fuertemente tipados"? Le he quitado el nombre a Haskell.
Como mencionó gsamaras, el estándar garantiza la misma alineación para las clases compatibles con el diseño .
Desafortunadamente, struct { T m; }
struct { T m; }
y T
no son compatibles con el diseño .
En 12.2.21, la norma establece los requisitos para una clase compatible con el diseño :
Dos tipos de estructura de disposición estándar (Clause 12) son clases compatibles con el diseño si su secuencia inicial común comprende todos los miembros y campos de bit de ambas clases (6.9).
Y la definición de secuencia inicial común está en 12.2.20:
La secuencia inicial común de dos tipos de estructura de disposición estándar (Clause 12) es la secuencia más larga de elementos de datos no estáticos y campos de bits en orden de declaración, comenzando con la primera de tales entidades en cada una de las estructuras, de modo que las entidades correspondientes tienen tipos compatibles con el diseño y ninguna de las dos es un campo de bits o ambos son campos de bits con el mismo ancho. [Ejemplo:
struct A { int a; char b; };
struct B { const int b1; volatile char b2; };
struct C { int c; unsigned : 0; char b; };
struct D { int d; char b : 4; };
struct E { unsigned int e; char b; };
La secuencia inicial común deA
yB
comprende todos los miembros de cualquier clase. La secuencia inicial común deA
yC
y deA
yD
comprende el primer miembro en cada caso. La secuencia inicial común deA
yE
está vacía.
- ejemplo final]
Entonces a partir de esto podemos hacer las siguientes observaciones importantes:
- La compatibilidad de diseño se limita estrictamente a las clases de diseño estándar. (O las enumeraciones usan el mismo tipo subyacente o el caso trivial cuando
T
yT2
son literalmente del mismo tipo. Véase 6.9.11.) En el caso general,T
no es una clase de diseño estándar. De hecho,T
ni siquiera es una clase en su ejemplo (esuint16_t
, lo crea o no, esto importa de acuerdo con el estándar). * - Incluso si se garantiza que
T
es una clase de diseño estándar,struct { T m; }
struct { T m; }
no tiene una secuencia inicial común conT
La secuencia destruct { T m; }
struct { T m; }
comienza conT
, mientras que la secuencia deT
comienza con los miembros de datos no estáticos deT
En realidad, se garantiza estrictamente que no es unaT
ya que una clase no puede contenerse por valor.
Por lo tanto, la garantía no puede ser retenida por la letra de la norma. Debería continuar realizando los iones static_assert
para asegurarse de que su compilador se comporte de la manera que espera.
* Consulte la mayoría de las preguntas sobre el tipo de juego de unión.
De acuerdo con la letra de la ley, el tamaño y la alineación de los tipos están definidos por la implementación y el estándar le brinda pocas garantías, si es que tiene alguna, sobre qué tamaño y alineación devolverá.
template <typename T, typename P>
struct Tagged {
T value;
};
En teoría , al compilador se le permite agregar relleno al final de esta estructura, lo que obviamente alteraría el tamaño y probablemente la alineación también. En la práctica, el único momento en que pude imaginar que esto sucediera es si a T
le dieron algún tipo de atributo "empaquetado" específico del compilador, pero Tagged
no lo era (pero incluso entonces, GCC parece funcionar bien ).
En cualquier caso, diría que sería una buena idea agregar algunas afirmaciones estáticas para garantizar que el compilador sea sensato, que es exactamente lo que has hecho :).
El estándar menciona en [basic.align]/1 :
Los tipos de objeto tienen requisitos de alineación (3.9.1, 3.9.2) que imponen restricciones a las direcciones a las que se puede asignar un objeto de ese tipo. Una alineación es un valor entero definido por la implementación que representa el número de bytes entre las direcciones sucesivas a las que se puede asignar un objeto determinado. Un tipo de objeto impone un requisito de alineación en cada objeto de ese tipo; se puede solicitar una alineación más estricta utilizando el especificador de alineación (7.6.2).
Además, [basic.compound]/3 , menciona:
La representación del valor de los tipos de puntero está definida por la implementación. Los punteros a tipos compatibles con el diseño deben tener los mismos requisitos de representación y alineación de valores (6.11). [Nota: los punteros a los tipos sobrealineados (6.11) no tienen representación especial, pero su rango de valores válidos está restringido por el requisito de alineación extendida].
Como resultado, hay una garantía de que los tipos compatibles con el diseño tienen la misma alineación.
struct { T m; }
struct { T m; }
y T
no son compatibles con el diseño.
Como se señala here , para que dos elementos sean compatibles con el diseño, ambos deben ser tipos de diseño estándar, y sus miembros de datos no estáticos deben aparecer con los mismos tipos y en el mismo orden.
struct { T m; }
struct { T m; }
contiene solo una T
, pero T
es una T
por lo que no puede contener una T
como su primer miembro de datos no estáticos.