Placement new base subobject of derived in C++

橙三吉。 提交于 2020-07-09 08:23:05

问题


Is it defined behavior to placement-new a trivially destructible base object of a derived?

struct base { int& ref; };
struct derived : public base {
    complicated_object complicated;
    derived(int& r, complicated_arg arg) :
            base {r}, complicated(arg) {}
};

unique_ptr<derived> rebind_ref(unique_ptr<derived>&& ptr,
                               int& ref) {
    // Change where the `ref` in the `base` subobject of
    // derived refers.
    return unique_ptr<derived>(static_cast<derived*>(
        ::new (static_cast<base*>(ptr.release()) base{ref}));
}

Note that I tried to structure rebind_ref to not break any strict aliasing assumptions a compiler might have made.


回答1:


No, this is not allowed by the C++ Standard, for at least two reasons.

The text that sometimes allows a placement-new of a new object into the storage for another object of the same type is found in [basic.life], paragraph 8. The bold emphasis is mine.

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • the storage for the new object exactly overlays the storage location which the original object occupied, and

  • the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and

  • 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

  • [C++17] the original object was a most derived object of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).

  • [C++20 draft 2018-10-09] neither the original object nor the new object is a potentially-overlapping subobject ([intro.object]).

The C++20 change is to account for the possibility of zero-size non-static data members, but it still also rules out all base class subobjects (empty or not). "Potentially-overlapping subobject" is a new term defined in [intro.object] paragraph 7:

A potentially-overlapping subobject is either:

  • a base class subobject, or

  • a non-static data member declared with the no_unique_address attribute ([dcl.attr.nouniqueaddr]).

(Even if you do find some way to rearrange things to avoid the reference member and base class issues, remember to make sure nobody can ever define a const derived variable, for example by making all constructors private!)




回答2:


static_cast<derived*>(
        ::new (&something) base{ref})

is not valid, by definition new (...) base(...) creates a base object as a new complete object, that can be considered an existing complete object or member subobject sometimes (under conditions that aren't met by base anyway) but never a base subobject.

There is no existing rule that says you can pretend new (addr) base creates a valid derived object just because the base object is overwriting another base base subobject. If there previously was a derived object, you have just reused its storage with new (addr) base. Even if by some magic the derived object was still in existence, the result of the evaluation of the new expression wouldn't point to it, it would point to a base complete object.

If you want to pretend you did something (like creating a derived object), without actually doing it (calling a derived constructor), you can pour some volatile qualifiers on pointers to force the compiler to erase all assumptions on values and compile the code as if there was an ABI transition.



来源:https://stackoverflow.com/questions/52884257/placement-new-base-subobject-of-derived-in-c

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