Spec-Zone.ru › C++

std::launder

Определён в заголовочном файле <new>
template< class T >
constexpr T* launder( T* p ) noexcept;
(начиная с C++17)

Барьер деvirtualизации в отношении 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:

  • Получение указателя на объект, созданный в области памяти существующего объекта того же типа, когда указатели на старый объект нельзя использовать повторно (например, если один из объектов является подобъектом базового класса);
  • Получение указателя на объект, созданный с помощью оператора размещения 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/cpp/utility/launder

Spec-Zone.ru

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