Overwriting an object with an object of same type

只谈情不闲聊 提交于 2019-11-28 13:51:27
Tony D

(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 to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the underlying bytes (1.7) making up obj1 are copied into obj2, obj2 shall subsequently hold the same value as obj1. [ 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 is const-qualified or a reference type ...
  • using 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 a void*".

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 at p, 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() 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.

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 a Const*."

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 at p

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!