[1]
Are there any cases in which the addition of p0593r6 into C++20 (§ 6.7.2.11 Object model [intro.object]) made std::launder
not necessary, where the same use case in C++17 required std::launder
, or are they completely orthogonal?
[2]
The example in the spec for [ptr::launder] is:
struct X { int n; };
const X *p = new const X{3};
const int a = p->n;
new (const_cast<X*>(p)) const X{5}; // p does not point to new object ([basic.life]) because its type is const
const int b = p->n; // undefined behavior
const int c = std::launder(p)->n; // OK
Another example is given by @Nicol Bolas in this SO answer, using a pointer that points to a valid storage but of a different type:
aligned_storage<sizeof(int), alignof(int)>::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));
Are there other use cases, not related to allowing casting of two objects which are not transparently replaceable, for using std::launder
?
Specifically:
- Would reinterpret_cast from A* to B*, both are pointer-interconvertible, may require using
std::launder
in any case? (i.e. can two pointers be pointer-interconvertible and yet not be transparently replaceable? the spec didn't relate between these two terms). - Does reinterpret_cast from void* to T* require using
std::launder
? - Does the following code below require use of
std::launder
? If so, under which case in the spec does it fall to require that?
A struct with reference member, inspired by this discussion:
struct A {
constexpr A(int &x) : ref(x) {}
int &ref;
};
int main() {
int n1 = 1, n2 = 2;
A a { n1 };
a.~A();
new (&a) A {n2};
a.ref = 3; // do we need to launder somebody here?
std::cout << a.ref << ' ' << n1 << ' ' << n2 << std::endl;
}
Before C++17, a pointer with a given address and type always pointed to an object of that type located at that address, provided that the code respects the rules of [basic.life]. (see: Is a pointer with the right address and type still always a valid pointer since C++17?).
But in the C++17 standard added a new quality to a pointer value. This quality is not encode within the pointer type but qualifies directly the value, independently of the type (this is the case also of the traceability). It is described in [basic.compound]/3
This quality of a pointer value has its own semantic (transition rules), and for the case of
reinterpret_cast
it is described in the next paragraph:In [basic-life], we can find an other rule that describes how transitions this quality when an object storage is reused:
As you can see the quality "pointer to an object" is attached to a specific object.
That means that in the variation bellow of the first example you give, the
reinterpret_cast
does not allow us not to use the pointer optimization barrier:A
reinterpret_cast
is not a pointer optimization barrier:reinterpret_cast <int*>(p)
points to the member of the destroyed object.An other way to conceive it is that the "pointer to" quality is conserved by
reinterpret_cast
as long as the object are pointer inter-convertible or if its casted to void and then back to a pointer inter-convertible type. (See [exp.static_cast]/13). Soreinterpret_cast <int*>(reinterpret_cast <void*>(p))
still points to the destroyed object.For the last example you gives, the name
a
refers to a non const complete object, so the originala
is transparently replaceable by the new object.For the first question you ask: "Are there any cases in which the addition of p0593r6 into C++20 (§ 6.7.2.11 Object model [intro.object]) made std::launder not necessary, where the same use case in C++17 required std::launder, or are they completely orthogonal?"
Honestly, I have not been able to find any cases that where std::launder could compensate implict-lifetime objects. But I found an example were implicit-lifetime object makes std::launder usefull: