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
учасник буде 2.
Тож скажи мені ... що u.x.n
повернеться?
Очевидною відповіддю буде 2. Але це неправильно, оскільки компілятору дозволено припустити, що справді const
змінна (не просто a 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
дозволяє нам у бік цього кроку.
n
це const
змінна, компілятор вільний припустити, що u.x.n
завжди має бути 1." Де в стандарті це написано? Я прошу, бо сама проблема, на яку ви вказали, могла б означати, що вона в першу чергу помилкова. Це має бути істинним лише за правилом нібито, яке тут не вдається. Що я пропускаю?
ptr
представляє, ви порушите launder
передумову, тому немає сенсу говорити про результат.
memcpy
реінтерпретації на підтримуваних платформах (тобто в'яже вирівнювання) .
std::launder
?std::launder
використовується для "отримання вказівника на об'єкт, створений у сховищі, зайнятому існуючим об'єктом одного типу, навіть якщо він має const або посилання членів".