almacenamiento alineado y alias estrictos


Actualmente estoy usando aligned_storage para implementar un tipo 'Opcional' similar al de boost::optional. Para lograr esto tengo un miembro de la clase así:

typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;

Utilizo placement new para crear el objeto, sin embargo no almaceno el puntero devuelto en ninguna parte. En su lugar, puedo acceder al tipo subyacente del objeto en todas mis funciones miembro de esta manera (obviamente con comprobaciones para garantizar que el objeto sea válido a través de una bandera booleana también almacenada en mi tipo opcional):

T const* operator->() const {
    return static_cast<T const*>(static_cast<void const*>(&t_));
}

Mi la pregunta es si esto es seguro. Mi entendimiento es que mi uso de la colocación nueva cambia el 'tipo dinámico' del objeto, y mientras siga accediendo a la memoria usando ese tipo estaré bien. Sin embargo, no estoy claro si tengo que mantener el puntero devuelto desde la colocación nueva o si se me permite simplemente lanzar al tipo subyacente siempre que necesite acceder a él. He leído la sección 3.10 del estándar C++11, sin embargo no soy lo suficientemente fluido en standardese para ser asegúrese.

Si es posible, me sentiría mejor si pudiera hacer referencia al estándar en su respuesta (me ayuda a dormir por la noche :P).

Author: Jeffrey Bosboom, 2012-11-20

1 answers

ABICT su uso es seguro.

  • La colocación de un objeto nuevo de tipo T creará un objeto a partir de la dirección pasada.

§5.3.4 / 10 dice:

Una nueva expresión pasa la cantidad de espacio solicitada a la función de asignación como el primer argumento del tipo std:: size_t. That el argumento no será menor que el tamaño del objeto que se está creando; puede ser mayor que el tamaño del objeto que se crea sólo si el objeto es un matriz.

Para un objeto que no es una matriz, el tamaño asignado no puede ser mayor que el tamaño del objeto, por lo que la representación del objeto debe comenzar al principio de la memoria asignada para que quepa.

Placement new devuelve el puntero pasado (véase § 18.6.1.3/2) como resultado de la "asignación", por lo que la representación del objeto construido comenzará en esa dirección.

  • static_cast<> y conversiones implícitas entre T* type y void* convert entre un puntero al objeto y un puntero a su almacenamiento, si el objeto es un objeto completo.

§4.10 / 2 dice:

Un prvalue de tipo "puntero a cv T", donde T es un tipo de objeto, puede ser convertido a un prvalue de tipo "puntero a cv void". El resultado de convertir un "puntero a cv T "a un" puntero a cv void " apunta a la inicio de la ubicación de almacenamiento donde reside el objeto de tipo T, como si el objeto es un objeto más derivado (1.8) de tipo T [...]

Esto define la conversión implícita a convertir como se indica. Además §5.2.9 [expr.estática.cast] / 4 define static_cast<> para conversiones explícitas, donde una conversión implícita existe para tener el mismo efecto que la conversión implícita:

De lo contrario, una expresión e puede convertirse explícitamente a un tipo T utilizando un static_cast del formulario static_cast<T>(e) si la declaración T t(e); está bien formado, para algunas variables temporales inventadas t (8.5). El efecto de tal conversión explícita es lo mismo que realizar la declaración y la inicialización y luego el uso de la temporal variable como resultado de la conversión. [...]

Para el inverso static_cast<> (de void* a T*), §5.2.9/13 estados:

Un prvalue del tipo "puntero a cv1 void" se puede convertir en un prvalue de tipo "puntero a cv2 T", donde T es un tipo de objeto y cv2 es el igual cualificación cv o mayor que cv1. [...] Un valor de tipo puntero a objeto convertido en " puntero a cv void" y volver, posiblemente con diferente cv-calificación, tendrá su valor original.

Así que si tienes un void* apuntando al almacenamiento del objeto T (que es el valor del puntero que resultaría de la conversión implícita de un T* al objeto, entonces un static_cast de eso a un T* producirá un puntero válido al objeto.

Volviendo a su pregunta, los puntos anteriores implican que si usted tienen

typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;
void * pvt_ = &t_;

T* pT = new (&t_) T(args...);
void * pvT = pT;

Entonces

  • el almacenamiento de *pT superpone exactamente el primer tamaño (T) bytes del almacenamiento de t_, de modo que pvT == pvt_
  • pvt_ == static_cast<void*>(&t_)
  • static_cast<T*>(pvT) == pT
  • Tomados en conjunto que rinde static_cast<T*>(static_cast<void*>(&t_)) == pT
 12
Author: JoergB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-31 19:41:25