Is the following well defined?
#include <iostream>
#include <string.h>
using namespace std;
struct Const {
const int i;
Const (int i) : i(i) {}
int get0() { return 0; } // best accessor ever!
};
int main() {
Const *q,*p = new Const(1);
new (p) Const(2);
memcpy (&q, &p, sizeof p);
cout << q->i;
return 0;
}
Note that after construction of second Const
, p
doesn't semantically (intentionally?) points to new object, and the first is gone, so it is usable "as a void*
". But the second object is constructed at the exact same address, so the bit pattern of p
represents the address of the new object.
COMMENT
new (p) Const(2)
erase the old object stored at p
, so the pointer is not valid anymore, except as a pointer to storage (void*
).
I want to recover the value of p
as a Const*
.
COMMENT 2
After either p->~Const()
or memset (p, 0, sizeof *p)
it is clear that p
does not point to a valid object, so p
can only be used as pointer to storage (void*
or char*
), for example to reconstruct another object. At that point p->get0()
is not allowed.
Here the demolition of the old object is done by the constructor of the new one, but I don't think that makes a difference.
My intuition is that: In any case, the old object is gone, and p
points to the old object, not the new one.
I am looking for a confirmation or refutation based on the standard.
SEE ALSO
I have asked essentially the same question about pointers, in C and C++ :
Please read these discussions before answering "this is ridiculous".
(making community-wiki as incorporating dyp's comment re 3.8/7 is very significant; while my earlier analysis was correct I would have said much the same things about code that was broken, having overlooked 3.8/7 myself)
Const *q,*p = new Const(1);
new (p) Const(2);
The new(p) Const(2);
line overwrites the object that had been constructed with Const(1)
.
memcpy (&q, &p, sizeof p);
This is equivalent to q = p;
.
cout << q->i;
This accesses the q->i
member, which will be 2
.
The somewhat noteworthy things are:
std::memcpy
is an ugly way to assign p
to q
... it is legal though under 3.9/3:For any trivially copyable type
T
, if two pointers toT
point to distinctT
objectsobj1
andobj2
, where neitherobj1
norobj2
is a base-class subobject, if the underlying bytes (1.7) making upobj1
are copied intoobj2
,obj2
shall subsequently hold the same value asobj1
. [ Example:
T* t1p;
T* t2p;
// provided that t2p points to an initialized object ...
std::memcpy(t1p, t2p, sizeof(T));
// at this point, every subobject of trivially copyable type in *t1p contains
// the same value as the corresponding subobject in *t2p
The overwriting of the old Const(1)
object with Const(2)
is allowed as long as the program doesn't depend on side effects of the former's destructor, which it doesn't.
(as dyp noted in comments below) ongoing access to the Const(2)
object using p
is illegal under 3.8/7's third point:
pointer that pointed to the original object [...] can be used to manipulate the new object, if...
- 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 isconst
-qualified or a reference type ...
q
- rather than p
- to access i
is presumably necessary to avoid compiler optimisations based on presumed knowledge of i
.As for your commentary:
Note that after construction of second
Const
,p
doesn't semantically (intentionally?) points to new object, and the first is gone, so it is usable "as avoid*
".
Given you placement-new an object at the address contained in p
, p
most certainly does point to the newly created object, and very intentionally, but it can't be used to manipulate that object under 3.8/7 as above.
Given you seem to have a notion of "semantically pointing" that's not defined in C++ the truth of that part of the statement's in your own mind.
'after construction of second Const
, p
...is usable "as a void*
' makes no sense... it's not more usable as anything than it was beforehand.
But the second object is constructed at the exact same address, so the bit pattern of
p
represents the address of the new object.
Of course, but your comments show you think "bit pattern" is somehow distinct from the value of the pointer as applies to assignment with =
, which is not true.
new (p) Const(2)
erase the old object stored atp
, so the pointer is not valid anymore, except as a pointer to storage (void*
).
"erase" is a strange term for it... overwrites would be more meaningful. As dyp noted and explained above, 3.8/7 says you shouldn't "manipulate" the object p
points to after the placement new, but the value and type of the pointer are unaffected by the placmeent new. Much as you can call f(void*)
with a pointer to any type, the placement-new
doesn't need to know or care about the type of the p
expression.
After either
p->~Const()
ormemset (p, 0, sizeof *p)
it is clear thatp
does not point to a valid object, sop
can only be used as pointer to storage (void*
orchar*
), for example to reconstruct another object. At that pointp->get0()
is not allowed.
Most of that's true, if by "p
can only be used" you mean the value of p
at that time rather than the pointer itself (which can be of course also be assigned to). And you're trying to be a little too clever with the void*
/ char*
thing - p
remains a Const*
, even if it's only used by placement new which doesn't care about the pointee type.
"I want to recover the value of
p
as aConst*
."
The value of p
was not changed after it was first initialised. placement-new
uses the value - it does not modify it. There's nothing to recover as nothing was lost. That said, dyp's highlighted the need not to use p
to manipulate the object, so while the value wasn't lost it's not directly usable as wanted either.
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