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++ :
- Dereferencing an out of bound pointer that contains the address of an object (array of array)
- Is memcpy of a pointer the same as assignment?
- Are pointer variables just integers with some operators or are they "mystical"?
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 assignp
toq
... 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 withConst(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 usingp
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 ...
- using
q
- rather thanp
- to accessi
is presumably necessary to avoid compiler optimisations based on presumed knowledge ofi
.
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.
This is only intended as an addendum to @Tony D's answer, regarding
new (p) Const(2)
erase the old object stored atp
I think you need to differentiate between an object and the conceptual idea of an "instance".
[...] An object is a region of storage.[...]
[N4431 §1.8/1]
So the pointer p
points to a region of storage, which contains the bit pattern of some "instance" before the placement new and some different bit pattern of a different, but well constructed "instance" of the correct (same) type.
So at the location pointed to by p
there's a valid object, and when assigning q
from it q
points to it. Though as noted in the other answer, accessing it via p
isn't permited.
来源:https://stackoverflow.com/questions/32043314/overwriting-an-object-with-an-object-of-same-type