¿Es una violación estricta de alias al alias una estructura como su primer miembro?

Código de muestra:

struct S { int x; }; int func() { S s{2}; return (int &)s; // Equivalent to *reinterpret_cast(&s) } 

Creo que esto es común y se considera aceptable. El estándar garantiza que no hay un relleno inicial en la estructura. Sin embargo, este caso no aparece en la regla de alias estricta (C ++ 17 [basic.lval] / 11):

Si un progtwig intenta acceder al valor almacenado de un objeto a través de un glvalue distinto de uno de los siguientes tipos, el comportamiento no está definido:

  • (11.1) el tipo dynamic del objeto,
  • (11.2) una versión calificada de CV del tipo dynamic del objeto,
  • (11.3) un tipo similar (como se define en 7.5) al tipo dynamic del objeto,
  • (11.4) un tipo que es el tipo firmado o sin firmar correspondiente al tipo dynamic del objeto,
  • (11.5) un tipo que es el tipo firmado o sin firmar que corresponde a una versión calificada de cv del tipo dynamic del objeto,
  • (11.6) un tipo agregado o de unión que incluye uno de los tipos mencionados anteriormente entre sus elementos o miembros de datos no estáticos (incluido, recursivamente, un elemento o miembro de datos no estáticos de un subagregado o unión contenida),
  • (11.7) un tipo que es un tipo de clase base (posiblemente cv calificado) del tipo dynamic del objeto,
  • (11.8) un tipo char, unsigned char o std :: byte.

Parece claro que el objeto s tiene acceso a su valor almacenado.

Los tipos enumerados en los puntos de viñeta son el tipo de glvalue que realiza el acceso , no el tipo del objeto al que se accede. En este código, el tipo glvalue es int que no es un tipo agregado o de unión, descartando 11.6.

Mi pregunta es: ¿este código es correcto y, de ser así, bajo cuál de los puntos anteriores se permite?

El comportamiento del reparto se reduce a [expr.static.cast] / 13;

Un prvalor de tipo “puntero a cv1 void ” se puede convertir a un prvalor de tipo “puntero a cv2 T “, donde T es un tipo de objeto y cv2 tiene la misma calificación cv que la cv1 o una calificación mayor . Si el valor del puntero original representa la dirección A de un byte en la memoria y A no satisface el requisito de alineación de T , el valor del puntero resultante no se especifica. De lo contrario, si el valor del puntero original apunta a un objeto a , y hay un objeto b de tipo T (ignorando la calificación cv) que es puntero-interconvertible con a , el resultado es un puntero a b . De lo contrario, el valor del puntero no se modifica por la conversión.

La definición de puntero-interconvertible es:

Dos objetos a y b son punteros interconvertibles si:

  • son el mismo objeto, o
  • uno es un objeto de unión y el otro es un miembro de datos no estáticos de ese objeto, o
  • uno es un objeto de clase de diseño estándar y el otro es el primer miembro de datos no estáticos de ese objeto o, si el objeto no tiene miembros de datos no estáticos, el primer subobjeto de clase base de ese objeto, o
  • existe un objeto c tal que a y c son punteros interconvertibles, y cyb son punteros interconvertibles.

Por lo tanto, en el código original, s y sx son punteros interconvertibles y se deduce que (int &)s realidad designa sx .

Por lo tanto, en la regla de alias estricta, el objeto cuyo valor almacenado se está accediendo es sx y no s por lo que no hay problema, el código es correcto.

Creo que está en expr.reinterpret.cast # 11

Una expresión glvalue de tipo T1, que designa un objeto x , se puede convertir al tipo “referencia a T2” si una expresión de tipo “puntero a T1” se puede convertir explícitamente al tipo “puntero a T2” utilizando reinterpret_cast. El resultado es el de *reinterpret_cast(p) donde p es un puntero a x de tipo “puntero a T1” . No se crea ningún temporal, no se realiza ninguna copia y no se llama a constructores o funciones de conversión [1] .

[1] Esto se conoce a veces como un juego de palabras cuando el resultado se refiere al mismo objeto que el valor de la fuente glvalue

Respaldando la respuesta de @MM sobre puntero-incovertible :

de cppreference :

Suponiendo que se cumplan los requisitos de alineación, un reinterpret_cast no cambia el valor de un puntero fuera de unos pocos casos limitados relacionados con objetos punteros interconvertibles :

 struct S { int a; } s; int* p = reinterpret_cast(&s); // value of p is "pointer to sa" because sa // and s are pointer-interconvertible *p = 2; // sa is also 2 

versus

 struct S { int a; }; S s{2}; int i = (int &)s; // Equivalent to *reinterpret_cast(&s) // i doesn't change Sa;