std::launder
удачно назван, но только если вы знаете, для чего он нужен. Он выполняет отмывание памяти .
Рассмотрим пример в статье:
struct X { const int n; };
union U { X x; float f; };
...
U u = {{ 1 }};
Этот оператор выполняет агрегатную инициализацию, инициализируя первого члена U
с {1}
.
Потому что n
это const
переменная, компилятор волен считать , что u.x.n
должно всегда быть 1.
Итак, что произойдет, если мы сделаем это:
X *p = new (&u.x) X {2};
Поскольку X
это тривиально, нам не нужно уничтожать старый объект перед созданием нового на его месте, так что это совершенно законный код. Новый объект будет иметь n
член 2.
Так скажи мне ... что u.x.n
вернется?
Очевидный ответ будет 2. Но это неправильно, потому что компилятору разрешено предполагать, что истинная const
переменная (не просто объявленная const&
, а объявленная переменная объекта const
) никогда не изменится . Но мы просто изменили это.
[basic.life] / 8 разъясняет обстоятельства, когда можно получить доступ к вновь созданному объекту через переменные / указатели / ссылки на старый. И наличие const
члена является одним из дисквалифицирующих факторов.
Итак ... как мы можем говорить u.x.n
правильно?
Мы должны отмыть нашу память:
assert(*std::launder(&u.x.n) == 2); //Will be true.
Отмывание денег используется для предотвращения отслеживания людьми, откуда вы взяли свои деньги. Отмывание памяти используется для предотвращения отслеживания компилятором того, откуда вы получили ваш объект, что вынуждает его избегать любых оптимизаций, которые могут больше не применяться.
Еще один дисквалифицирующий фактор заключается в том, что вы меняете тип объекта. std::launder
может помочь и здесь:
aligned_storage<sizeof(int), alignof(int)>::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));
[basic.life] / 8 говорит нам, что если вы выделите новый объект в хранилище старого, вы не сможете получить доступ к новому объекту через указатели на старый. launder
позволяет нам обойти это.
std::launder
?std::launder
используется для «получения указателя на объект, созданный в хранилище, занятом существующим объектом того же типа, даже если он имеет константные или ссылочные члены».