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

P0137 presenta la plantilla de función std::launder y realiza muchos, muchos cambios en el estándar en las secciones relativas a uniones, duración y punteros.

¿Cuál es el problema que este artículo está resolviendo? ¿Cuáles son los cambios en el lenguaje que tengo que tener en cuenta? ¿Y qué estamos launder ?

std::launder tiene un nombre apropiado, 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 instrucción realiza inicialización agregada, inicializando el primer miembro de U con {1} .

Como n es una variable const , el comstackdor puede suponer que uxn siempre será 1.

Entonces, ¿qué pasa si hacemos esto?

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

Debido a que X es 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 n miembro ser 2.

Entonces dime … ¿qué no uxn ?

La respuesta obvia será 2. Pero eso está mal, porque el comstackdor puede suponer que una variable verdaderamente const (no simplemente una const& , sino una variable de objeto declarada const ) nunca cambiará . Pero simplemente lo cambiamos.

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

Entonces … ¿cómo podemos hablar sobre uxn correctamente?

Tenemos que lavar nuestra memoria:

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

El lavado de dinero se usa para evitar que las personas rastreen de dónde sacaste tu dinero. El lavado de memoria se usa para evitar que el comstackdor rastree de dónde sacó su objeto, lo que le obliga a evitar cualquier optimización que ya no se aplique.

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

 aligned_storage::type data; new(&data) int; int *p = std::launder(reinterpret_cast(&data)); 

[basic.life] / 8 nos dice que, si asigna un nuevo objeto en el almacenamiento del anterior, no puede acceder al nuevo objeto mediante punteros al antiguo. launder nos permite dejar de lado eso.