Reputation: 177
struct A
{
int x;
}
A t{};
t.x = 5;
new (&t) A;
// is it always safe to assume that t.x is 5?
assert(t.x == 5);
As far as I know, when a trivial object of class type is created, the compiler can omit the call of explicit or implicit default constructor because no initialization is required. (is that right?)
Then, If placement new is performed on a trivial object whose lifetime has already begun, is it guaranteed to preserve its object/value representation? (If so, I want to know where I can find the specification..)
Upvotes: 8
Views: 693
Reputation: 72281
From looking at the Standard, the program has undefined behavior because of an invalid use of an object with indeterminate value.
Per [basic.life]/8, since the object of type A
created by the placement new-expression exactly overlays the original object t
, using the name t
after that point refers to the A
object created by the new-expression.
In [basic.indet]/1, we have:
When storage for an object with automatic or dynamic storage duration is obtained, the object has an indeterminate value, and if no initialization is performed for the object, that object retains an indeterminate value until that value is replaced ([expr.ass]).
One important detail here (which I missed at first) is that "obtaining storage" is different from "allocating storage" or the storage duration of a storage region. The "obtain storage" words are also used to define the beginning of an object's lifetime in [basic.life]/1 and in the context of a new-expression in [expr.new]/10:
A new-expression may obtain storage for the object by calling an allocation function ([basic.stc.dynamic.allocation]). ... [ Note: ... The set of allocation and deallocation functions that may be called by a new-expression may include functions that do not perform allocation or deallocation; for example, see [new.delete.placement]. — end note ]
So the placement new-expression "obtains storage" for the object of type A
and its subobject of type int
when it calls operator new(void*)
. For this purpose, it doesn't make a difference that the memory locations in the storage region actually have static storage duration. Since "no initialization is performed" for the created subobject of type int
with dynamic storage duration, it has an indeterminate value.
See also this Q&A: What does it mean to obtain storage?
Upvotes: 3
Reputation: 119144
Well, let's ask some compilers for their opinion. Reading an indeterminate value is UB, which means that if it occurs inside a constant expression, it must be diagnosed. We can't directly use placement new
in a constant expression, but we can use std::construct_at
(which has a typed interface). I also modified the class A
slightly so that value-initialization does the same thing as default-initialization:
#include <memory>
struct A
{
int x;
constexpr A() {}
};
constexpr int foo() {
A t;
t.x = 5;
std::construct_at(&t);
return t.x;
}
static_assert(foo() == 5);
As you can see on Godbolt, Clang, ICC, and MSVC all reject the code, saying that foo()
is not a constant expression. Clang and MSVC additionally indicate that they have a problem with the read of t.x
, which they consider to be a read of an uninitialized value.
P0593, while not directly related to this issue, contains an explanation that seems relevant:
The properties ascribed to objects and references throughout this document apply for a given object or reference only during its lifetime.
That is, reusing the storage occupied by an object in order to create a new object always destroys whatever value was held by the old object, because an object's value dies with its lifetime. Now, objects of type A
are transparently replaceable by other objects of type A
, so it is permitted to continue to use the name t
even after its storage has been reused. That does not imply that the new t
holds the value that the old t
does. It only means that t
is not a dangling reference to the old object.
Going off what is said in P0593, GCC is wrong and the other compilers are right. In constant expression evaluation, this kind of code is required to be diagnosed. Otherwise, it's just UB.
Upvotes: 8