Какова цель std :: launder?

242

P0137 представляет шаблон функции std::launderи вносит множество изменений в стандарт в разделах, касающихся объединений, времени жизни и указателей.

Какую проблему решает эта статья? Какие изменения в языке я должен знать? И что мы думаем launder?

Барри
источник
2
Вы спрашиваете о самой газете или о std::launder? std::launderиспользуется для «получения указателя на объект, созданный в хранилище, занятом существующим объектом того же типа, даже если он имеет константные или ссылочные члены».
txtechhelp
7
полезная ссылка на эту тему. Также этот вопрос stackoverflow.com/questions/27003727/…
Пол Руни
Это было теперь выпущено в VC2017 в версии 15.7.0
Дамиан
Согласно стандарту, указатели являются тривиальными типами, поэтому отмывание ничего не делает. ;)
curiousguy

Ответы:

250

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позволяет нам обойти это.

Николь Болас
источник
34
Так правильно ли мой tl; dr правильно: "отмывание в основном для наказания не UB типа"?
дружески
13
Не могли бы вы объяснить, почему это так? «Потому что nэто constпеременная, компилятор волен считать , что u.x.nвсегда должно быть 1.» Где в стандарте это сказано? Я спрашиваю, потому что сама проблема, на которую вы указали, может показаться, что она изначально ложна. Это должно быть верно только в соответствии с правилом «как будто», которое здесь не выполняется. Чего мне не хватает?
user541686 8.09.16
10
@Mehrdad [basic.life] / 8: « Если, [...] новый объект создается в месте хранения, которое исходный объект занимал [...], то имя исходного объекта будет автоматически ссылаться на новый объект [...] если: [...] тип [...] не содержит нестатического члена данных, тип которого является константным или ссылочным типом [...] "
ecatmur
14
@ Барри Очень; если по указанному адресу нет объектов типа T ptr, то вы нарушаете launderпредварительное условие, поэтому нет смысла говорить о результате.
TC
17
@NicolBolas Можно только надеяться, что суперкат лоббирует комитеты столько же, сколько и бесконечно требовательных ответов от сторонних пользователей языка на SO. Кроме того, хороший оптимизирующий компилятор в любом случае оптимизирует правильное решение для memcpyреинтерпретации на месте на поддерживаемых (т. Е. Слабое выравнивание) платформах .
underscore_d