Spec-Zone.ru › C++

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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API