Basic.life/8 tells us that we can use the storage occupied by an object to create a new one after its lifetime has ended and use its original name to refer to it unless:
- the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and [...]
emphasis mine
But, just below that we can see a note saying that:
- If these conditions are not met, a pointer to the new object can be obtained from a pointer that represents the address of its storage by calling
std::launder
This explains the purposes of std::launder
. We can have a class type that has const
members and use placement-new to create a new object there with different internal values.
What surprises me is the first part of the first quote. It clearly indicates that if the storage is const
(not necessarily contains const
members, but the whole object is declared as const
), we cannot use it to refer to a new object as is, but it may imply that std::launder
may fix it.
But how would we create such object? The simplest example fails:
const auto x = 5;
new (&x) auto(10);
It's because we cannot use const void*
as a buffer for placement-new. We could const_cast
it, but casting away a "true" const
ness is Undefined Behaviour. I believe this is also the case here.
I would understand if there simply was a prohibition of using const
objects as placement-new buffer, but if that would be the case, what would be the purpose of the emphasised part of the first quote? Can we use reuse a truly const
object's storage for a different object?
Apparently all it took was to look just 2 items below the part of the standard I linked to. Basic.life/10 tells us that:
Creating a new object within the storage that a const complete object with static, thread, or automatic storage duration occupies, or within the storage that such a const object used to occupy before its lifetime ended, results in undefined behavior.
And it comes with an example:
struct B {
B();
~B();
};
const B b;
void h() {
b.~B();
new (const_cast<B*>(&b)) const B; // undefined behavior
}
which ultimately leads me to a conclusion that it's illegal to use placement-new
on a memory occupied by a truly const
object. Thus, I believe that the note mentioned in the question (in reference to point 8) is faulty - it should exclude the case in question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With