P0137 представляет шаблон функции std::launder
и вносит множество изменений в стандарт в разделах, касающихся объединений, времени жизни и указателей.
Какую проблему решает эта статья? Какие изменения в языке я должен знать? А что мы launder
?
P0137 представляет шаблон функции std::launder
и вносит множество изменений в стандарт в разделах, касающихся объединений, времени жизни и указателей.
Какую проблему решает эта статья? Какие изменения в языке я должен знать? А что мы launder
?
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
be 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
позволяет нам сделать шаг вперед.