std::launder(3)
β¨ std::launder(3) β C++ Standard Libary β¨
okay bestie, this is std::launder(3) from the C++ Standard Libary (yes, "Libary" is literally the manual name, we are not fixing it, we are respecting the iconique typo π
). It's a tiny C++ standard library function with big "provenance fence" energy, and the whole page is about when you can get a shiny pointer to an object that is assumed to have a distinct lifetime and dynamic type. no cap.
πΈ NAME πΈ
std::launder - std::launder
β¨ Synopsis β¨
Defined in header <new>:
template< class T > (since C++17)
constexpr T* launder( T* p ) noexcept; (until C++20)
template< class T > (since C++20)
[[nodiscard]] constexpr T* launder( T* p ) noexcept;
Provenance fence with respect to p. Returns a pointer to the same memory that p points to, but where the referent object is assumed to have a distinct lifetime and dynamic type.
Formally, given:
- the pointer
prepresents the addressAof a byte in memory - an object
xis located at the addressA xis within its lifetime- the type of
xis 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 objectyif those bytes are within the storage of an objectzthat is pointer-interconvertible withy, or within the immediately enclosing array of whichzis 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 and only if the (converted) value of its argument may be used in place of the function invocation. In other words, std::launder does not relax restrictions in constant evaluation.
π 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. (The [[nodiscard]] in the synopsis is giving "bestie, do not ignore me" energy, fr fr.)
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
newfrom 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. iykyk.
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 π¦
The example starts with // Run this code (slay), and then:
#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
}
π Defect reports π
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
- DR
LWG 2859, applied toC++17β behavior as published: definition of reachable did not consider pointer arithmetic from pointer-interconvertible object; correct behavior: included. - DR
LWG 3495, applied toC++17β behavior as published:std::laundermight make pointer to an inactive member dereferenceable in constant expression; correct behavior: forbidden.
okay that's the whole tea π source/version: http://cppreference.com, date: 2024.06.10, page: std::launder(3). byeee bestie, go launder responsibly πβ¨π
π¦ where this page comes from
- nixpkgs unstable: stdman 2024.07.05
- opensuse-leap 16.0: stdman 2024.07.05-bp160.1.9 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms