std::launder
Определено в заголовочном файле <new> | ||
|---|---|---|
template< class T > constexpr T* launder( T* p ) noexcept; |
(с C++17) (до C++20) | |
template< class T > [[nodiscard]] constexpr T* launder( T* p ) noexcept; | (с C++20) |
Защита происхождения относительно p. Возвращает указатель на ту же память, на которую указывает p, но при этом предполагается, что объект-ссылка имеет различный срок жизни и динамический тип.
Формально, задано
- указатель
pпредставляет адресAбайта в памяти - объект
xрасположен по адресуA -
xнаходится в пределах своего срока жизни - тип
xтакой же, какT, игнорируя cv-квалификаторы на каждом уровне - каждый байт, который можно достичь через результат, достижим через p (байты достижимы через указатель, который указывает на объект
y, если эти байты находятся в хранилище объектаz, который совместим по указателям сy, или внутри непосредственно окружающего массива, элементом которого являетсяz).
Тогда std::launder(p) возвращает значение типа T*, которое указывает на объект x. В противном случае поведение не определено.
Программа некорректна, если T является типом функции или (возможно, cv-квалифицированным) void.
std::launder может использоваться в ядре выражения константы, только если (преобразованное) значение его аргумента может быть использовано вместо вызова функции. Другими словами, std::launder не ослабляет ограничения при оценке констант.
Примечания
std::launder не оказывает никакого влияния на свой аргумент. Его возвращаемое значение должно использоваться для доступа к объекту. Таким образом, отбрасывание возвращаемого значения всегда является ошибкой.
Типичные применения std::launder включают:
- Получение указателя на объект, созданный в хранилище существующего объекта того же типа, где указатели на старый объект не могут быть переиспользованы (например, потому что либо объект является подобъектом базового класса);
- Получение указателя на объект, созданный с помощью размещения
newиз указателя на объект, предоставляющий хранилище для этого объекта.
Ограничение достижимости гарантирует, что std::launder не может использоваться для доступа к байтам, недоступным через исходный указатель, тем самым препятствуя анализу утечек компилятором.
int x[10];
auto p = std::launder(reinterpret_cast<int(*)[10]>(&x[0])); // OK
int x2[2][10];
auto p2 = std::launder(reinterpret_cast<int(*)[10]>(&x2[0][0]));
// Undefined behavior: x2[1] would be reachable through the resulting pointer to x2[0]
// but is not reachable from the source
struct X { int a[10]; } x3, x4[2]; // standard layout; assume no padding
auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x3.a[0])); // OK
auto p4 = std::launder(reinterpret_cast<int(*)[10]>(&x4[0].a[0]));
// Undefined behavior: x4[1] would be reachable through the resulting pointer to x4[0].a
// (which is pointer-interconvertible with x4[0]) but is not reachable from the source
struct Y { int a[10]; double y; } x5;
auto p5 = std::launder(reinterpret_cast<int(*)[10]>(&x5.a[0]));
// Undefined behavior: x5.y would be reachable through the resulting pointer to x5.a
// but is not reachable from the sourceПример
#include <cassert>
#include <cstddef>
#include <new>
struct Base
{
virtual int transmogrify();
};
struct Derived : Base
{
int transmogrify() override
{
new(this) Base;
return 2;
}
};
int Base::transmogrify()
{
new(this) Derived;
return 1;
}
static_assert(sizeof(Derived) == sizeof(Base));
int main()
{
// Case 1: the new object failed to be transparently replaceable because
// it is a base subobject but the old object is a complete object.
Base base;
int n = base.transmogrify();
// int m = base.transmogrify(); // undefined behavior
int m = std::launder(&base)->transmogrify(); // OK
assert(m + n == 3);
// Case 2: access to a new object whose storage is provided
// by a byte array through a pointer to the array.
struct Y { int z; };
alignas(Y) std::byte s[sizeof(Y)];
Y* q = new(&s) Y{2};
const int f = reinterpret_cast<Y*>(&s)->z; // Class member access is undefined
// behavior: reinterpret_cast<Y*>(&s)
// has value "pointer to s" and does
// not point to a Y object
const int g = q->z; // OK
const int h = std::launder(reinterpret_cast<Y*>(&s))->z; // OK
[](...){}(f, g, h); // evokes [[maybe_unused]] effect
}Отчеты об ошибках
Следующие отчеты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применено к | Поведение, как опубликовано | Правильное поведение |
|---|---|---|---|
| LWG 2859 | C++17 | определение достижимости не учитывало арифметику указателей из объекта, совместимого по указателям | включено |
| LWG 3495 | C++17 |
std::launder может сделать указатель на неактивныйчлен доступным для обращений в выражении константы | запрещено |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/utility/launder