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

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

Barry

Ответы (2)

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::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast(&data));

[basic.life] / 8 сообщает нам, что если вы разместите новый объект в хранилище старого, вы не сможете получить доступ к новому объекту через указатели на старый.launder позволяет нам обойти это.

std :: launder - неправильное обозначение. Эта функция выполняет , противоположное отмыванию: Она загрязняет указанную память, чтобы удалить любые ожидания компилятора в отношении указанного значения. Это исключает любую оптимизацию компилятора, основанную на таких ожиданиях.

Таким образом, в ответе @NicolBolas компилятор может предположить, что некоторая память содержит некоторое постоянное значение; или не инициализирован. Вы говорите компилятору: «Это место (сейчас) загрязнено, не делайте этого предположения».

Если вам интересно, почему компилятор всегда всегда придерживался своих наивных ожиданий, и вам нужно было бы заметно испортить ему ситуацию - вы можете прочитать это обсуждение:

Зачем вводить `std :: launder` вместо того, чтобы об этом позаботился компилятор?

... что привело меня к такому мнению о том, что означает std :: launder.

2022 WebDevInsider