Namespaces
Variants

std::launder

From ru.cppreference.net
 
 
Библиотека утилит
Поддержка языка
Поддержка типов (базовые типы, RTTI)
Макросы проверки возможностей библиотеки (C++20)
Утилиты программы
Функции с переменным числом аргументов
Поддержка корутин (C++20)
Поддержка контрактов (C++26)
Трёхстороннее сравнение
(C++20)
(C++20)(C++20)(C++20)    
(C++20)(C++20)(C++20)

Общие утилиты
Операторы отношений (устарело в C++20)
 
Библиотека управления памятью
Распределители
Ресурсы памяти
Неинициализированное хранилище (до C++20*)
Поддержка сборщика мусора (до C++23)
 
 
Определено в заголовке <new>
template< class T >
constexpr T* launder( T* p ) noexcept;
(начиная с C++17)

Барьер девиртуализации по отношению к p. Возвращает указатель на объект по тому же адресу, который представляет 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 включают:

  • Получение указателя на объект, созданный в области памяти существующего объекта того же типа, когда указатели на старый объект не могут быть повторно использованы (например, когда любой из объектов является подобъектом базового класса);
  • Получение указателя на объект, созданный через placement new , из указателя на объект, предоставляющий память для этого объекта.

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

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]));
// Неопределённое поведение: x2[1] был бы достижим через результирующий указатель на x2[0]
// но не достижим из исходного объекта
struct X { int a[10]; } x3, x4[2]; // стандартная компоновка; предполагаем отсутствие заполнения
auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x3.a[0])); // OK
auto p4 = std::launder(reinterpret_cast<int(*)[10]>(&x4[0].a[0]));
// Неопределённое поведение: x4[1] был бы достижим через результирующий указатель на x4[0].a
// (который является указательно-взаимопреобразуемым с x4[0]) но не достижим из исходного объекта
struct Y { int a[10]; double y; } x5;
auto p5 = std::launder(reinterpret_cast<int(*)[10]>(&x5.a[0]));
// Неопределённое поведение: x5.y был бы достижим через результирующий указатель на x5.a
// но не достижим из исходного объекта

Пример

#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()
{
    // Случай 1: новый объект не может быть прозрачно заменяемым, потому что
    // он является базовым подобъектом, а старый объект является полным объектом.
    Base base;
    int n = base.transmogrify();
    // int m = base.transmogrify(); // неопределенное поведение
    int m = std::launder(&base)->transmogrify(); // OK
    assert(m + n == 3);
    // Случай 2: доступ к новому объекту, хранилище которого предоставлено
    // массивом байтов, через указатель на массив.
    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; // Доступ к члену класса является неопределенным
                                               // поведением: reinterpret_cast<Y*>(&s)
                                               // имеет значение "указатель на s" и не
                                               // указывает на объект Y
    const int g = q->z; // OK
    const int h = std::launder(reinterpret_cast<Y*>(&s))->z; // OK
    [](...){}(f, g, h); // создает эффект [[maybe_unused]]
}

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Applied to Behavior as published Correct behavior
LWG 2859 C++17 определение reachable не учитывало арифметику указателей
от указательно-интерконвертируемого объекта
включено
LWG 3495 C++17 std::launder может сделать указатель на неактивный
член разыменовываемым в константном выражении
запрещено