std::launder
Defined in header <new>
|
||
template <class T> constexpr T* launder(T* p) noexcept; |
(since C++17) (until C++20) |
|
template <class T> [[nodiscard]] constexpr T* launder(T* p) noexcept; |
(since C++20) | |
Obtains a pointer to the object located at the address represented by p
.
Formally, given
- the pointer
p
represents the addressA
of a byte in memory - an object
X
is located at the addressA
-
X
is within its lifetime - the type of
X
is the same asT
, ignoring cv-qualifiers at every level - every byte that would be reachable through the result is reachable through p (bytes are reachable through a pointer that points to an object
Y
if those bytes are within the storage of an objectZ
that is pointer-interconvertible withY
, or within the immediately enclosing array of whichZ
is an element)
Then std::launder(p)
returns a value of type T*
that points to the object X
. Otherwise, the behavior is undefined.
The program is ill-formed if T
is a function type or (possibly cv-qualified) void
.
std::launder
may be used in a core constant expression if the value of its argument may be used in a core constant expression.
Notes
std::launder
has no effect on its argument. Its return value must be used to access the object. Thus, it's always an error to discard the return value.
Typical uses of std::launder
include:
- Obtaining a pointer to an object created in the storage of an existing object of the same type, where pointers to the old object cannot be reused (for instance, because either object is a base class subobject);
- Obtaining a pointer to an object created by placement
new
from a pointer to an object providing storage for that object.
The reachability restriction ensures that std::launder
cannot be used to access bytes not accessible through the original pointer, thereby interfering with the compiler's escape analysis.
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
Example
#include <new> #include <cstddef> #include <cassert> struct X { const int n; // note: X has a const member int m; }; struct Y { int z; }; struct A { virtual int transmogrify(); }; struct B : A { int transmogrify() override { new(this) A; return 2; } }; int A::transmogrify() { new(this) B; return 1; } static_assert(sizeof(B) == sizeof(A)); int main() { // Case 1: the new object failed to be transparently replaceable because of // a subobject of const-qualified or reference type. // Note: this case is a defect fixed via RU007 (included in P1971R0), // so std::launder is no longer needed in this case since RU007. X *p = new X{3, 4}; const int a = p->n; X* np = new (p) X{5, 6}; // p does not point to new object because X::n is const // until RU007; np points to the new object; so does p since RU007 const int b = p->n; // undefined behavior until RU007; OK since RU007 const int c = p->m; // undefined behavior until RU007: // even though m is non-const, p can't be used; OK since RU007 const int d = std::launder(p)->n; // OK: std::launder(p) points to new object const int e = np->n; // OK // Case 2: the new object failed to be transparently replaceable because either // it or the original object is potentially-overlapping (it is a base subobject // or a member subobject declared with [[no_unique_address]]). A i; int n = i.transmogrify(); // int m = i.transmogrify(); // undefined behavior int m = std::launder(&i)->transmogrify(); // OK assert(m + n == 3); // Case 3: access to a new object whose storage is provided by a byte array through // a pointer to the array. 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 }
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
LWG 2859 | C++17 | definition of reachable didn't consider pointer-arithmetic from pointer-interconvertible object | included |