Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which union member becomes active after placement new

Regarding this code:

#include <string>

int main()
{
    union u {
        u() { i = 0; }
        ~u() {}

        int i;
        std::string s1;
        std::string s2;
    } u;

    new (&u) std::string{};
}

[intro.object]/2 says that

Objects can contain other objects, called subobjects. A subobject can be a member subobject ([class.mem]), a base class subobject ([class.derived]), or an array element. An object that is not a subobject of any other object is called a complete object. If an object is created in storage associated with a member subobject or array element e (which may or may not be within its lifetime), the created object is a subobject of e's containing object if:
— the lifetime of e's containing object has begun and not ended, and
— the storage for the new object exactly overlays the storage location associated with e, and
— the new object is of the same type as e
(ignoring cv-qualification).

There is no requirement how an object is created in the storage associated with a member subobject. The code doesn't have to nominate the subobject in the argument of the address-of operator if the subobject is a member of a standard-layout union or the first member of a non-union class object. It is enough to get the address of the containing object to designate the storage of the member subobject in such cases.

«There is no requirement how an object is created», among other things, means that the pointer given to placement new does not have to point to the subobject. Mainly because there could be no object to point to (note, the [intro.object]/2 do not require subobject to be alive). In std-discussion mailing list it was asked, given an object x of type struct A { unsigned char buf[1]; };, is there a difference between new (&x) A{} and new (x.buf) A{}? And the answer was no, in both cases, x.buf would provide storage for A{}. Because

The wording in [intro.object] and [basic.life] concern themselves with the storage address represented by a pointer, not the object to which it points.


[class.union]/1 swears that «At most one of the non-static data members of an object of union type can be active at any time».

Which one became active in the code above, s1 or s2?

like image 292
Language Lawyer Avatar asked Jan 17 '19 13:01

Language Lawyer


1 Answers

A pointer is an address, but to the object model, it is more than an address. It points to a specific object at that address. Multiple objects can exist at a certain address, but that doesn't mean that pointers to any of those objects are simultaneously pointers to other objects at that address. Consider what [expr.unary.op]/1 says of pointer indirection:

the result is an lvalue referring to the object or function to which the expression points.

Not to "an object at that address"; it is an lvalue referring to the object being pointed to. So clearly, in the C++ object model, multiple objects can exist at the same address, but a specific pointer into that address does not point to all of those objects. It only points to one of them.

[expr.unary.op]/2 says "The result of the unary & operator is a pointer to its operand". Therefore, &u points to u, which is of type u (BTW, was it really necessary to name the object the same as the type?). &u does not point to u.i, u.s1 or u.s2. All of those are guaranteed to share the same address as &u, but &u itself only points to u.

So the question now becomes, what is the storage represented by &u? Well, per [intro.object]/1, we know that "An object occupies a region of storage". If &u points to the object u, that pointer must therefore represent the region of storage occupied by that object. Not the storage of any of its subobjects; it is the storage for that object. In its entirety.

Now, we get to new(&u) std::string{}. This expression creates an object of type std::string{}, within the storage represented by &u. That represents reusing the storage of the object u. Which in accord with [basic.life]/1.4, terminates the lifetime of u. Which terminates the lifetime of its active member subobject.

So the answer to your question is that neither becomes active, because the object u doesn't exist anymore.

like image 79
Nicol Bolas Avatar answered Oct 20 '22 15:10

Nicol Bolas