diferencias - c++17
ColocaciĆ³n nueva en std:: alineado_storage? (2)
La forma más paranoica es
::new ((void *)::std::addressof(storage)) T(a, b, c);
Explicación:
-
::std::addressof
guardias contra eloperator&
unario sobrecargadooperator&
enstorage
, técnicamente permitido por la norma. (Aunque ninguna implementación sensata lo haría).::std
protege contra cualquier espacio de nombres (o clases) que no sea de nivel superior llamadostd
que pueda estar dentro del alcance. -
(void *)
(que en este caso es el equivalente a unstatic_cast
) garantiza que usted llame aloperator new
ubicaciónoperator new
tome unvoid *
lugar de otra cosa comodecltype(storage) *
. -
::new
omite cualquieroperator new
colocación de clase específica, asegurando que la llamada se dirija a la global.
Juntos, esto garantiza que la llamada se dirija al operator new
colocación de la biblioteca operator new
tomando un void *
, y que la T
se construya en el lugar donde se encuentra el storage
.
En la mayoría de los programas sanos, sin embargo,
new (&storage) T(a,b,c);
debería ser suficiente
Supongamos que tengo un tipo de plantilla de parámetro T.
Y supongamos que tengo un std::aligned_storage
como sigue:
typename std::aligned_storage<sizeof(T), alignof(T)>::type storage;
Quiero colocar una nueva T en el storage
.
¿Cuál es el valor / tipo de puntero que cumple con la norma para pasar al nuevo operador de colocación, y cómo se obtiene eso del storage
?
new (& ???) T(a,b,c);
Por ejemplo:
new (&storage) T(a,b,c);
new (static_cast<void*>(&storage)) T(a,b,c);
new (reinterpret_cast<T*>(&storage)) T(a,b,c);
new (static_cast<T*>(static_cast<void*>(&storage));
¿Cuál de los anteriores (si corresponde) cumple, y si ninguno, cuál es la mejor manera?
La función de asignación de ubicación se describe a continuación (C ++ 14 n4140 18.6.1.3):
void* operator new(std::size_t size, void* ptr) noexcept;
Devoluciones:
ptr
.Observaciones: Intencionalmente no realiza ninguna otra acción.
20.10.7.6 la tabla 57 describe el aligned_storage<Len, Align>
así:
El
type
typedef miembro será un tipo POD adecuado para usar como almacenamiento no inicializado para cualquier objeto cuyo tamaño sea a lo sumo Len y cuya alineación sea un divisor de Alinear.
Esto implica que, en su caso, &storage
se alinea adecuadamente para contener un objeto de tipo T
Por lo tanto, en circunstancias normales 1 , las 4 formas en que ha enumerado la ubicación de la llamada new
son válidas y equivalentes. Yo usaría el primero ( new (&storage)
) por brevedad.
1 TC señaló correctamente en los comentarios que es técnicamente posible que su programa declare una sobrecarga de la función de asignación tomando un typename std::aligned_storage<sizeof(T), alignof(T)>::type*
, que luego se seleccionará por resolución de sobrecarga en lugar de la versión ''nueva ubicación'' provista por la biblioteca.
Yo diría que esto es poco probable en al menos el 99,999% de los casos, pero si también necesita protegerse contra eso, use uno de los modelos para void*
. El static_cast<void*>(&storage)
directo static_cast<void*>(&storage)
es suficiente.
Además, si está paranoico a este nivel, probablemente debería usar ::new
lugar de simplemente new
para omitir cualquier función de asignación específica de la clase.