Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/utility/launder"

From cppreference.com
< cpp‎ | utility
m (Example: shorter, though less universal.)
(Expand the possible problematic description.)
 
(12 intermediate revisions by 6 users not shown)
Line 1: Line 1:
 
{{cpp/title|launder}}
 
{{cpp/title|launder}}
{{cpp/utility/navbar}}
+
{{cpp/memory/new/navbar}}
 
{{dcl begin}}
 
{{dcl begin}}
{{dcl header | new}}
+
{{dcl header|new}}
{{dcl rev multi|since1=c++17 |dcl1=
+
{{dcl|since=c++17|
template <class T>
+
template< class T >
constexpr T* launder(T* p) noexcept;
+
constexpr T* launder( T* p ) noexcept;
|since2=c++20|dcl2=
+
template <class T>
+
[[nodiscard]] constexpr T* launder(T* p) noexcept;
+
 
}}
 
}}
 
{{dcl end}}
 
{{dcl end}}
  
Obtains a pointer to the object located at the address represented by {{tt|p}}.  
+
Devirtualization fence with respect to {{c|p}}. Returns a pointer to an object at the same address that {{c|p}} represents, while the object can be a new base class subobject whose most derived class is different from that of the original {{c|*p}} object.
  
 
Formally, given
 
Formally, given
* the pointer {{tt|p}} represents the address {{tt|A}} of a byte in memory
+
* the pointer {{c|p}} represents the address {{tt|A}} of a byte in memory
* an object {{tt|X}} is located at the address {{tt|A}}
+
* an object {{c|x}} is located at the address {{tt|A}}
* {{tt|X}} is within its [[cpp/language/lifetime|lifetime]]
+
* {{c|x}} is within its [[cpp/language/lifetime|lifetime]]
* the type of {{tt|X}} is the same as {{tt|T}}, ignoring cv-qualifiers at every level
+
* the type of {{c|x}} is the same as {{tt|T}}, 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 {{tt|Y}} if those bytes are within the storage of an object {{tt|Z}} that is [[cpp/language/static_cast#pointer-interconvertible|pointer-interconvertible]] with {{tt|Y}}, or within the immediately enclosing array of which {{tt|Z}} is an element).
+
* every byte that would be reachable through the result is reachable through p (bytes are reachable through a pointer that points to an object {{c|y}} if those bytes are within the storage of an object {{c|z}} that is [[cpp/language/static_cast#pointer-interconvertible|pointer-interconvertible]] with {{c|y}}, or within the immediately enclosing array of which {{c|z}} is an element).
  
Then {{c|std::launder(p)}} returns a value of type {{tt|T*}} that points to the object {{tt|X}}. Otherwise, the behavior is undefined.
+
Then {{c|std::launder(p)}} returns a value of type {{tt|T*}} that points to the object {{c|x}}. Otherwise, the behavior is undefined.
  
The program is ill-formed if {{tt|T}} is a function type or (possibly cv-qualified) {{tt|void}}.
+
The program is ill-formed if {{tt|T}} is a function type or (possibly cv-qualified) {{c/core|void}}.
  
 
{{tt|std::launder}} may be used in a [[cpp/language/constant expression|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, {{tt|std::launder}} does not relax restrictions in constant evaluation.
 
{{tt|std::launder}} may be used in a [[cpp/language/constant expression|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, {{tt|std::launder}} does not relax restrictions in constant evaluation.
Line 41: Line 38:
  
 
int x2[2][10];
 
int x2[2][10];
auto p2 = std::launder(reinterpret_cast<int(*)[10]>(&x2[0][0]));  
+
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]
 
// Undefined behavior: x2[1] would be reachable through the resulting pointer to x2[0]
 
// but is not reachable from the source
 
// but is not reachable from the source
Line 47: Line 44:
 
struct X { int a[10]; } x3, x4[2]; // standard layout; assume no padding
 
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 p3 = std::launder(reinterpret_cast<int(*)[10]>(&x3.a[0])); // OK
auto p4 = std::launder(reinterpret_cast<int(*)[10]>(&x4[0].a[0]));  
+
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  
+
// 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  
+
// (which is pointer-interconvertible with x4[0]) but is not reachable from the source
  
 
struct Y { int a[10]; double y; } x5;
 
struct Y { int a[10]; double y; } x5;
auto p5 = std::launder(reinterpret_cast<int(*)[10]>(&x5.a[0]));  
+
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
 
// Undefined behavior: x5.y would be reachable through the resulting pointer to x5.a
 
// but is not reachable from the source
 
// but is not reachable from the source
Line 59: Line 56:
 
===Example===
 
===Example===
 
{{example
 
{{example
|
+
|code=
| code=
+
#include <new>
+
#include <cstddef>
+
 
#include <cassert>
 
#include <cassert>
+
#include <cstddef>
struct X {
+
#include <new>
  const int n; // note: X has a const member
+
 
  int m;
+
struct Base
};
+
{
+
struct Y {
+
  int z;
+
};
+
+
struct A {  
+
 
     virtual int transmogrify();
 
     virtual int transmogrify();
 
};
 
};
+
 
struct B : A {
+
struct Derived : Base
     int transmogrify() override { new(this) A; return 2; }
+
{
 +
     int transmogrify() override
 +
    {
 +
        new(this) Base;
 +
        return 2;
 +
    }
 
};
 
};
+
 
int A::transmogrify() { new(this) B; return 1; }
+
int Base::transmogrify()
+
static_assert(sizeof(B) == sizeof(A));
+
+
int main()
+
 
{
 
{
  // Case 1: the new object failed to be transparently replaceable because it is a
+
    new(this) Derived;
  // base subobject but the old object is a complete object.
+
    return 1;
  A i;
+
}
  int n = i.transmogrify();
+
  // int m = i.transmogrify(); // undefined behavior
+
  int m = std::launder(&i)->transmogrify(); // OK
+
  assert(m + n == 3);
+
  
  // Case 2: access to a new object whose storage is provided by a byte array through
+
static_assert(sizeof(Derived) == sizeof(Base));
  // 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
+
  
  [f, g, h]{}; // suppresses "unused-variable" warnings
+
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 ===
+
===Defect reports===
 
{{dr list begin}}
 
{{dr list begin}}
{{dr list item | wg=lwg | dr=2859 | std=C++17 | before=definition of ''reachable'' didn't consider pointer-arithmetic from pointer-interconvertible object | after=included }}
+
{{dr list item|wg=lwg|dr=2859|std=C++17|before=definition of ''reachable'' did not consider pointer<br>arithmetic from pointer-interconvertible object|after=included}}
{{dr list item | wg=lwg | dr=3495 | std=C++17 | before={{tt|launder}} might make pointer to an inactive member dereferenceable in constant expression | after=forbidden }}
+
{{dr list item|wg=lwg|dr=3495|std=C++17|before={{tt|std::launder}} might make pointer to an inactive<br>member dereferenceable in constant expression|after=forbidden}}
 
{{dr list end}}
 
{{dr list end}}
  
{{langlinks|es|ja|ru|zh}}
+
{{langlinks|de|es|ja|ru|zh}}

Latest revision as of 01:31, 30 October 2024

 
 
Utilities library
General utilities
Relational operators (deprecated in C++20)
 
Dynamic memory management
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Allocators
Garbage collection support
(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)



 
 
Defined in header <new>
template< class T >
constexpr T* launder( T* p ) noexcept;
(since C++17)

Devirtualization fence with respect to p. Returns a pointer to an object at the same address that p represents, while the object can be a new base class subobject whose most derived class is different from that of the original *p object.

Formally, given

  • the pointer p represents the address A of a byte in memory
  • an object x is located at the address A
  • x is within its lifetime
  • the type of x is the same as T, 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 object z that is pointer-interconvertible with y, or within the immediately enclosing array of which z 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 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.

[edit] 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

[edit] Example

#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
}

[edit] 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 did not consider pointer
arithmetic from pointer-interconvertible object
included
LWG 3495 C++17 std::launder might make pointer to an inactive
member dereferenceable in constant expression
forbidden