cplusplus / CWG

Core Working Group
24 stars 7 forks source link

[class.mi] What object are indirect bases subobjects of? #375

Open randomnetcat opened 1 year ago

randomnetcat commented 1 year ago

Full name of submitter (unless configured in github; will be published with the issue): Janet Cobb

Reference (section label): [class.mi]/4

Link to reflector thread (if any): N/A

Issue description:

[class.mi]/4 reads, in part:

For each distinct occurrence of a non-virtual base class in the class lattice of the most derived class, the most derived object ([intro.object]) shall contain a corresponding distinct base class subobject of that type. For each distinct base class that is specified virtual, the most derived object shall contain a single base class subobject of that type.

This appears to suggest that indirect bases are subobjects "contained" within the most derived object ("class lattice" doesn't appear to be defined, so it's slightly hard to tell).

[intro.object]/2 reads, in part:

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.

This suggests the relationship of "contain"ing is meant to mean only and exactly "being a direct subobject of", which would imply that [class.mi] means indirect bases are direct subobjects of the most derived object. This is supported by [intro.object]/5.2, which reads "Otherwise, the complete object of x is the complete object of the (unique) object that contains x.", which explicitly states that an object can be "contained" only within one other object.

Taking this all together results in indirect bases being subobjects only of the most derived object, and not of other base class subobjects.

That is, in

struct A {};
struct B : A {};
struct C : B {};

a most derived object of type C would have two base class subobjects, one of type B and one of type A, while the base class subobject of type B would not have any base class subobjects.

This appears to conflict with other parts of the standard, e.g. [conv.ptr]/3, which reads:

A prvalue of type “pointer to cv D”, where D is a complete class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class ([class.derived]) of D. [...] The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the null pointer value of the destination type.

Considering the expression static_cast<A*>(static_cast<B*>(&some_c)), this clause expects that the base class subobject of type B has a base class subobject of type A, which is not consistent with this reading of [class.mi].

Relatedly, note 5 in [class.mi] reads, in part:

For an object c of class type C, a single subobject of type V is shared by every base class subobject of c that has a virtual base class of type V. Given the class C defined above, an object of class C will have one subobject of class V, as shown in Figure 4.

This also appears to be inconsistent with the normative text that a subobject can only be contained within one other object, as it suggests that a single object of type V is a subobject of multiple base class subobjects.

Suggested resolution: [TODO]

frederick-vs-ja commented 1 year ago

I think the major issue is that the containing relationship between objects is not properly defined (via italic texts), and there're conflicting uses.

Personally, I want distinct terms meaning direct subobjects, possibly indirect subobjects, (unique) direct containing object, and possibly indirect containing objects. It seems that only "direct subobject" is currently identified by the term subobject ([intro.object]/2).

jensmaurer commented 1 year ago

[class.mi]/4 doesn't say "direct" somewhere, so this could be read as "the most derived object (transitively) contains a distinct (possibly indirect) base class subobject for each (possibly indirect) base class".

That reading is consistent with [class.derived.general] p3, which is not limited to most derived objects.

randomnetcat commented 1 year ago

That's inconsistent with [intro.object]/5.2, which says that only a single object X can "contain" any given object Y, but [class.mi]/4 saying "shall contain, possibly transitively, a corresponding distinct base class" would solve the problem for non-virtual bases.

In any case I think there's still ambiguity for virtual bases?