c++ language-lawyer strict-aliasing reinterpret-cast

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



language-lawyer strict-aliasing (2)

Código de muestra:

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

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

Si un programa 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 dinámico del objeto,
  • (11.2) una versión calificada por cv del tipo dinámico del objeto,
  • (11.3) un tipo similar (como se define en 7.5) al tipo dinámico del objeto,
  • (11.4) un tipo que es el tipo firmado o sin firmar correspondiente al tipo dinámico 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 dinámico 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 dinámico 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 de 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?


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" usando un reinterpret_cast. El resultado es el de *reinterpret_cast<T2 *>(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 refiere a veces como un juego de palabras cuando el resultado se refiere al mismo objeto que el valor de la fuente

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<int*>(&s); // value of p is "pointer to s.a" because s.a // and s are pointer-interconvertible *p = 2; // s.a is also 2

versus

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


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

Un prvalue 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 es la misma calificación de cv o una calificación de cv1 mayor que la de cv1 . Si el valor del puntero original representa la dirección A de un byte en la memoria y A no cumple con 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.

Así que en el código original, s y sx son punteros interconvertibles y se sigue 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.