sintaxis semantica relacion entre ejemplos definicion c++ language-lawyer reinterpret-cast strict-aliasing placement-new

c++ - semantica - ¿Existe una diferencia(semántica) entre el valor de retorno de la ubicación nueva y el valor emitido de su operando?



sintaxis y semantica ejemplos (2)

El acceso a través de a es legal mientras que b no lo es. De [basic.compound]

Dos objetos b son interconvertibles por puntero si:

  • son el mismo objeto, o

  • uno es un objeto de unión de diseño estándar y el otro es un miembro de datos no estático 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 ([class.mem] ) o

  • existe un objeto c tal que c son interconvertibles por puntero, c y b son interconvertibles por puntero.

Si dos objetos son interconvertibles por puntero, tienen la misma dirección y es posible obtener un puntero a uno de un puntero a otro a través de un reinterpret_cast . [Nota: un objeto de matriz y su primer elemento no son interconvertibles por puntero, aunque tengan la misma dirección. - nota final]

No son el mismo objeto, ni uniones ni subobjetos entre sí, por lo tanto, no son interconvertibles por puntero.

Nota [expr.reinterpret.cast] solo garantiza reinterpret_cast<char*>(b) == buffer .

Un puntero de objeto puede convertirse explícitamente en un puntero de objeto de un tipo diferente. Cuando un valor pr v del tipo de puntero de objeto se convierte en el tipo de puntero de objeto "puntero a cv T ", el resultado es static_cast<cv T*>(static_cast<cv void*>(v)) . [Nota: Convertir un prvalor de tipo "puntero a T1 " en el tipo "puntero a T2 " (donde T1 y T2 son tipos de objeto y donde los requisitos de alineación de T2 no son más estrictos que los de T1 ) y volver a su tipo original produce el valor del puntero original. - nota final]

¿Existe una diferencia (semántica) entre el valor de retorno de la ubicación nueva y el valor emitido de su operando?

struct Foo { ... }; char buffer[...]; Foo *a = new(buffer) Foo; Foo *b = reinterpret_cast<Foo *>(buffer);

¿A y b difieren de alguna manera?

EDITAR: Basado en el comentario de DaBler, esta pregunta dice que hay una diferencia, si se usan miembros const / reference: colocación nueva y asignación de clase con miembro const

Entonces, mi pregunta a poco actualizada: ¿A y b difieren de alguna manera, si Foo no tiene miembros constantes o de referencia?


Solo se puede usar de forma segura para acceder directamente al objeto Foo creado por la nueva expresión de ubicación (que llamaremos x para facilitar la referencia). El uso de b requiere std::launder .

El valor de a se especifica en [expr.new]/1 :

Si la entidad es un objeto sin matriz, el resultado de la nueva expresión es un puntero al objeto creado.

El valor de a es, por lo tanto, "puntero a x ". Este puntero, por supuesto, se puede usar de forma segura para acceder a x .

reinterpret_cast<Foo*>(buffer) aplica la conversión de matriz a puntero a buffer (ver [expr.reinterpret.cast]/1 ). El valor resultante después de aplicar la conversión es "puntero al primer elemento del buffer ". Este es un reinterpret_cast de un puntero de objeto a un puntero de objeto de un tipo diferente, y se define como equivalente a static_cast<Foo*>(static_cast<void*>(buffer)) por [expr.reinterpret.cast]/7 .

El lanzamiento interno a void* es en realidad una conversión implícita. Por [conv.ptr]/2 ,

El valor del puntero no cambia con esta conversión.

Por lo tanto, el molde interno produce un void* con el valor "puntero al primer elemento del buffer ".

El molde externo se rige por [expr.static.cast]/13 , que he formateado ligeramente en viñetas:

Un valor prva de tipo "puntero a void cv1 " se puede convertir en un valor prolo de tipo "puntero a cv2 T ", donde T es un tipo de objeto y cv2 es la misma calificación cv o mayor calificación cv que cv1 .

  • 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 , entonces el valor del puntero resultante no está especificado.

  • 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 interconvertible por puntero con a , el resultado es un puntero a b .

  • De lo contrario, la conversión no modifica el valor del puntero.

Suponiendo que el buffer esté alineado adecuadamente (si no lo fuera, estaría en problemas mucho antes de este punto), la primera viñeta no es aplicable. La segunda viñeta también es inaplicable ya que no hay pointer-interconvertiblity aquí. De esto se deduce que tocamos la tercera viñeta: "el valor del puntero no ha cambiado por la conversión" y sigue siendo "puntero al primer elemento del buffer ".

Por lo tanto, b no apunta al objeto Foo x ; señala, en cambio, el primer elemento char del buffer , aunque su tipo es Foo* . Por lo tanto, no se puede usar para acceder a x ; intentar hacerlo produce un comportamiento indefinido (para el caso de miembro de datos no estático, por omisión de [expr.ref] ; para el caso de función de miembro no estático, por [class.mfct.non-static]/2 ).

Para recuperar un puntero a x desde b , se puede usar std::launder :

b = std::launder(b); // value of b is now "pointer to x" // and can be used to access x