Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/utility/launder"

From cppreference.com
< cpp‎ | utility
m (Changed first variable with name "a" to "pn" in order to avoid a naming conflict with the second definition of "a".)
(Example: also demo using the pointer returned by placement new)
Line 39: Line 39:
 
#include <cstddef>
 
#include <cstddef>
 
#include <cassert>
 
#include <cassert>
 
+
 
struct X {
 
struct X {
 
   const int n; // note: X has a const member
 
   const int n; // note: X has a const member
 
   int m;
 
   int m;
 
};
 
};
 
+
 
struct Y {
 
struct Y {
 
   int z;
 
   int z;
 
};
 
};
 
+
 
struct A {  
 
struct A {  
 
     virtual int transmogrify();
 
     virtual int transmogrify();
 
};
 
};
 
+
 
struct B : A {
 
struct B : A {
 
     int transmogrify() override { new(this) A; return 2; }
 
     int transmogrify() override { new(this) A; return 2; }
 
};
 
};
 
+
 
int A::transmogrify() { new(this) B; return 1; }
 
int A::transmogrify() { new(this) B; return 1; }
 
+
 
static_assert(sizeof(B) == sizeof(A));
 
static_assert(sizeof(B) == sizeof(A));
 
+
 
int main()
 
int main()
 
{
 
{
 
   X *p = new X{3, 4};
 
   X *p = new X{3, 4};
   const int pn = p->n;
+
   const int a = p->n;
   new (p) X{5, 6};    // p does not point to new object because X::n is const
+
   X* np = new (p) X{5, 6};    // p does not point to new object because X::n is const; np does
 
   const int b = p->n; // undefined behavior
 
   const int b = p->n; // undefined behavior
   const int x = p->m; // undefined behavior (even though m is non-const, p can't be used)
+
   const int c = p->m; // undefined behavior (even though m is non-const, p can't be used)
   const int c = std::launder(p)->n; // OK, std::launder(p) points to new object
+
   const int d = std::launder(p)->n; // OK, std::launder(p) points to new object
 
+
  const int e = np->n; // OK
 +
 
   alignas(Y) std::byte s[sizeof(Y)];
 
   alignas(Y) std::byte s[sizeof(Y)];
 
   Y* q = new(&s) Y{2};
 
   Y* q = new(&s) Y{2};
   const int d = reinterpret_cast<Y*>(&s)->z; // Class member access is undefined behavior:
+
   const int f = reinterpret_cast<Y*>(&s)->z; // Class member access is undefined behavior:
 
                                             // reinterpret_cast<Y*>(&s) has value "pointer to s"
 
                                             // reinterpret_cast<Y*>(&s) has value "pointer to s"
 
                                             // and does not point to a Y object  
 
                                             // and does not point to a Y object  
   const int e = q->z; // OK
+
   const int g = q->z; // OK
   const int f = std::launder(reinterpret_cast<Y*>(&s))->z; // OK
+
   const int h = std::launder(reinterpret_cast<Y*>(&s))->z; // OK
 
+
   A a;
+
   A i;
   int n = a.transmogrify();
+
   int n = i.transmogrify();
   // int m = a.transmogrify(); // undefined behavior
+
   // int m = i.transmogrify(); // undefined behavior
   int m = std::launder(&a)->transmogrify(); // OK
+
   int m = std::launder(&i)->transmogrify(); // OK
 
   assert(m + n == 3);
 
   assert(m + n == 3);
 
}
 
}

Revision as of 11:01, 26 February 2018

 
 
Utilities library
General utilities
Relational operators (deprecated in C++20)
 
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 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 the value of its argument may be used in a core constant expression

Notes

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 because the object has const or reference data members, or 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.

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()
{
  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; np does
  const int b = p->n; // undefined behavior
  const int c = p->m; // undefined behavior (even though m is non-const, p can't be used)
  const int d = std::launder(p)->n; // OK, std::launder(p) points to new object
  const int e = np->n; // OK
 
  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
 
  A i;
  int n = i.transmogrify();
  // int m = i.transmogrify(); // undefined behavior
  int m = std::launder(&i)->transmogrify(); // OK
  assert(m + n == 3);
}