¿Cuál es el propósito de std :: launder?

242

P0137 presenta la plantilla de función std::laundery realiza muchos, muchos cambios al estándar en las secciones relativas a uniones, vida útil y punteros.

¿Cuál es el problema que este documento está resolviendo? ¿Cuáles son los cambios en el idioma que debo tener en cuenta? ¿Y qué estamos launderhaciendo?

Barry
fuente
2
¿Estás preguntando sobre el papel en sí o sobre std::launder? std::launderse utiliza para "obtener un puntero a un objeto creado en el almacenamiento ocupado por un objeto existente del mismo tipo, incluso si tiene miembros constantes o de referencia".
txtechhelp
77
enlace útil sobre el tema. También esta pregunta stackoverflow.com/questions/27003727/…
Paul Rooney
Esto ahora se ha lanzado en VC2017 en la versión 15.7.0
Damian
Según el estándar, los punteros son tipos triviales, por lo que el lavado no hace nada. ;)
curiousguy

Respuestas:

250

std::laundertiene un nombre adecuado, aunque solo si sabes para qué sirve. Realiza lavado de memoria .

Considere el ejemplo en el documento:

struct X { const int n; };
union U { X x; float f; };
...

U u = {{ 1 }};

Esa declaración realiza una inicialización agregada, inicializando el primer miembro de Uwith {1}.

Debido a que nes una constvariable, el compilador es libre de asumir que u.x.ndebe siempre ser 1.

Entonces, ¿qué sucede si hacemos esto?

X *p = new (&u.x) X {2};

Como Xes trivial, no necesitamos destruir el objeto antiguo antes de crear uno nuevo en su lugar, por lo que este es un código perfectamente legal. El nuevo objeto tendrá su nmiembro 2.

Entonces dime ... ¿qué volverá u.x.n?

La respuesta obvia será 2. Pero eso está mal, porque el compilador puede asumir que una constvariable verdaderamente (no solo una const&, sino una variable de objeto declarada const ) nunca cambiará . Pero lo acabamos de cambiar.

[basic.life] / 8 explica las circunstancias en las que está bien acceder al objeto recién creado a través de variables / punteros / referencias al antiguo. Y tener un constmiembro es uno de los factores descalificadores.

Entonces ... ¿cómo podemos hablar u.x.nadecuadamente?

Tenemos que lavar nuestra memoria:

assert(*std::launder(&u.x.n) == 2); //Will be true.

El lavado de dinero se utiliza para evitar que las personas rastreen de dónde obtuvo su dinero. El lavado de memoria se utiliza para evitar que el compilador rastree de dónde obtuvo su objeto, lo que lo obliga a evitar cualquier optimización que ya no se aplique.

Otro de los factores descalificadores es si cambia el tipo de objeto. std::launderpuede ayudar aquí también:

aligned_storage<sizeof(int), alignof(int)>::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));

[basic.life] / 8 nos dice que, si asigna un nuevo objeto en el almacenamiento del antiguo, no puede acceder al nuevo objeto a través de punteros al antiguo. laundernos permite esquivar eso.

Nicol Bolas
fuente
34
Entonces, ¿mi tl; dr es correcto: "el lavado es básicamente para juegos de palabras que no son UB"?
druckermanly
13
¿Podría explicar por qué esto es cierto? "Debido a que nes una constvariable, el compilador es libre de asumir que u.x.nsiempre será 1." ¿Dónde en el estándar dice eso? Pregunto porque el mismo problema que usted señaló parece implicarme que es falso en primer lugar. Solo debería ser cierto bajo la regla as-if, que falla aquí. ¿Qué me estoy perdiendo?
user541686
10
@Mehrdad [basic.life] / 8: " Si, [...] se crea un nuevo objeto en la ubicación de almacenamiento donde el objeto original [...] ocupó el nombre del objeto original se referirá automáticamente al nuevo objeto [...] si: [...] el tipo [...] no contiene ningún miembro de datos no estático cuyo tipo esté calificado o sea un tipo de referencia [...] "
ecatmur
14
@Barry Very; si no hay objetos de tipo T ubicados en la dirección que ptrrepresenta, entonces se rompe launderla condición previa, por lo que no tiene sentido hablar del resultado.
TC
17
@NicolBolas Uno solo puede esperar que supercat ejerza tanta presión sobre los Comités como lo hacen exigiendo interminablemente respuestas de otros usuarios de idiomas de terceros en SO. Además, un buen compilador optimizador optimizará su solución correcta memcpyen una reinterpretación in situ en plataformas compatibles (es decir, alineación laxa) de todos modos .
underscore_d