Namespaces
Variants

std:: launder

From cppreference.net
Utilities library
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
Definiert im Header <new>
template < class T >
constexpr T * launder ( T * p ) noexcept ;
(seit C++17)

Devirtualisierungs-Barriere in Bezug auf p . Gibt einen Zeiger auf ein Objekt an derselben Adresse zurück, die p repräsentiert, wobei das Objekt ein neues Basisklassen-Subobjekt sein kann, dessen am meisten abgeleitete Klasse sich von der des ursprünglichen * p -Objekts unterscheidet.

Formal gegeben

  • der Zeiger p repräsentiert die Adresse A eines Bytes im Speicher
  • ein Objekt x befindet sich an der Adresse A
  • x befindet sich innerhalb seiner Lebensdauer
  • der Typ von x ist derselbe wie T , wobei CV-Qualifizierer auf jeder Ebene ignoriert werden
  • jedes Byte, das durch das Ergebnis erreichbar wäre, ist durch p erreichbar (Bytes sind durch einen Zeiger erreichbar, der auf ein Objekt y zeigt, wenn diese Bytes innerhalb des Speichers eines Objekts z liegen, das zeigerinterkonvertierbar mit y ist, oder innerhalb des unmittelbar umgebenden Arrays, von dem z ein Element ist).

Dann gibt std :: launder ( p ) einen Wert vom Typ T* zurück, der auf das Objekt x zeigt. Andernfalls ist das Verhalten undefiniert.

Das Programm ist fehlerhaft, wenn T ein Funktionstyp oder (möglicherweise cv-qualifiziertes) void ist.

std::launder kann in einem Core Constant Expression verwendet werden, wenn und nur wenn der (konvertierte) Wert seines Arguments anstelle des Funktionsaufrufs verwendet werden kann. Mit anderen Worten, std::launder lockert die Einschränkungen bei der Konstantenauswertung nicht.

Hinweise

std::launder hat keine Auswirkung auf sein Argument. Sein Rückgabewert muss verwendet werden, um auf das Objekt zuzugreifen. Daher ist es immer ein Fehler, den Rückgabewert zu verwerfen.

Typische Anwendungen von std::launder umfassen:

  • Erhalten eines Zeigers auf ein Objekt, das im Speicher eines bestehenden Objekts desselben Typs erstellt wurde, wobei Zeiger auf das alte Objekt nicht wiederverwendet werden können (beispielsweise weil eines der Objekte ein Basisklassen-Subobjekt ist);
  • Erhalten eines Zeigers auf ein Objekt, das durch Placement- new erstellt wurde, aus einem Zeiger auf ein Objekt, das den Speicher für dieses Objekt bereitstellt.

Die reachability -Einschränkung stellt sicher, dass std::launder nicht verwendet werden kann, um auf Bytes zuzugreifen, die nicht durch den ursprünglichen Zeiger erreichbar sind, und stört dadurch die Escape-Analyse des Compilers.

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]));
// Undefiniertes Verhalten: x2[1] wäre über den resultierenden Zeiger auf x2[0] erreichbar
// ist aber von der Quelle aus nicht erreichbar
struct X { int a[10]; } x3, x4[2]; // Standard-Layout; ohne Padding angenommen
auto p3 = std::launder(reinterpret_cast<int(*)[10]>(&x3.a[0])); // OK
auto p4 = std::launder(reinterpret_cast<int(*)[10]>(&x4[0].a[0]));
// Undefiniertes Verhalten: x4[1] wäre über den resultierenden Zeiger auf x4[0].a
// (der zeigerinterkonvertierbar mit x4[0] ist) erreichbar, ist aber von der Quelle aus nicht erreichbar
struct Y { int a[10]; double y; } x5;
auto p5 = std::launder(reinterpret_cast<int(*)[10]>(&x5.a[0]));
// Undefiniertes Verhalten: x5.y wäre über den resultierenden Zeiger auf x5.a
// erreichbar, ist aber von der Quelle aus nicht erreichbar

Beispiel

#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()
{
    // Fall 1: Das neue Objekt konnte nicht transparent ersetzt werden, weil
    // es ein Basis-Subobjekt ist, aber das alte Objekt ein vollständiges Objekt ist.
    Base base;
    int n = base.transmogrify();
    // int m = base.transmogrify(); // undefiniertes Verhalten
    int m = std::launder(&base)->transmogrify(); // OK
    assert(m + n == 3);
    // Fall 2: Zugriff auf ein neues Objekt, dessen Speicher
    // durch ein Byte-Array bereitgestellt wird, über einen Zeiger auf das 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; // Klassenmember-Zugriff ist undefiniertes
                                               // Verhalten: reinterpret_cast<Y*>(&s)
                                               // hat den Wert "Zeiger auf s" und zeigt
                                               // nicht auf ein Y-Objekt
    const int g = q->z; // OK
    const int h = std::launder(reinterpret_cast<Y*>(&s))->z; // OK
    [](...){}(f, g, h); // erzeugt [[maybe_unused]]-Effekt
}

Fehlerberichte

Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR Angewendet auf Verhalten wie veröffentlicht Korrektes Verhalten
LWG 2859 C++17 Definition von reachable berücksichtigte keine Pointer-Arithmetik
von pointer-interconvertiblem Objekt
eingeschlossen
LWG 3495 C++17 std::launder könnte Pointer auf inaktives
Mitglied in konstantem Ausdruck dereferenzierbar machen
verboten