Пространства имён
Варианты
Действия

std::launder

Материал из cppreference.com
 
 
Библиотека утилит
Языковая поддержка
Поддержка типов (базовые типы, RTTI)
Макросы тестирования функциональности библиотеки (C++20)    
Управление динамической памятью
Программные утилиты
Поддержка сопрограмм (C++20)
Вариативные функции
Трёхстороннее сравнение (C++20)
(C++20)
(C++20)(C++20)(C++20)(C++20)(C++20)(C++20)
Общие утилиты
Дата и время
Функциональные объекты
Библиотека форматирования (C++20)
(C++11)
Операторы отношения (устарело в C++20)
Целочисленные функции сравнения
(C++20)(C++20)(C++20)    
(C++20)
Операции обмена и типа
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
Общие лексические типы
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++17)
(C++23)
Элементарные преобразования строк
(C++17)
(C++17)
 
Динамическое управление памятью
no section name
Ограниченные алгоритмы неинициализированной памяти
no section name
Поддержка сбора мусора
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)



no section name
 
 
<tbody> </tbody> <tbody class="t-dcl-rev "> </tbody><tbody> </tbody>
Определено в заголовочном файле <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)

Provenance fence по отношению к 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 нельзя использовать для доступа к байтам, недоступным через исходный указатель, тем самым мешая 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++:

Номер Применён Поведение в стандарте Корректное поведение
LWG 2859 C++17 определение достижимости не учитывало арифметику указателя из
взаимопреобразуемого по указателю объекта
включено
LWG 3495 C++17 std::launder может сделать указатель на неактивный элемент
разыменовываемым в константном выражении
запрещено