6
votes

A standard-layout class is defined in [class]/7 in C++14, as follows (emphasis is mine):

A standard-layout class is a class that:

  • (7.1) — has no non-static data members of type non-standard-layout class (or array of such types) or reference,
  • (7.2) — has no virtual functions (10.3) and no virtual base classes (10.1),
  • (7.3) — has the same access control (Clause 11) for all non-static data members,
  • (7.4) — has no non-standard-layout base classes,
  • (7.5) — either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
  • (7.6) — has no base classes of the same type as the first non-static data member.

My concern is in relation to bullet (7.5): how is it possible to have no non-static data members in the most derived class and at the same time having one base class with non-static data members?

In other words, aren't the non-static data members of the base class, also non-static data members of the most-derived class?

1
Think of objects as the russian matruska dolls. The most derived object is the outer shell of the doll. While the most base object is at its core.101010
In the end though, they are different dolls.101010
Do you mean Matryoshka doll?bipll
@bipll Yeap that ones :P101010

1 Answers

9
votes

Yes, this is a defect in C++14, specifically CWG 1813. Although it can be repaired by reading "non-static data members" to refer only to direct (non-inherited) non-static data members (as is probably necessary elsewhere), the chosen fix has been to replace the language you found problematic with the following:

A standard-layout class is a class that: [...]

  • has all non-static data members and bit-fields in the class and its base classes first declared in the same class, [...]

This is a little tricky to get right; there is some resistance to the idea of considering inherited members as members of the derived class, even though [class.derived] has:

[...] Unless redeclared in the derived class, members of a base class are also considered to be members of the derived class. Members of a base class other than constructors are said to be inherited by the derived class. [...]

Notwithstanding this, a number of places where both direct and inherited non-static data members are intended to be considered specifically call out inherited data members, for example also in [class] (after the resolution to CWG 1672):

8.6 - an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members (including, recursively, an element or non-static data member of a subaggregate or contained union),

Or in [basic.lval]:

7.8 - If X is a non-union class type, the set M(X) is empty if X has no (possibly inherited (Clause 10)) non-static data members; [...]

Meanwhile, in many places "members" has to be read as referring only to direct members to make sense; to begin with and only looking at non-static data members (not member functions), [expr.rel]/3.2, [expr.const]/5.1, [dcl.constexpr]/4.6, [class.mem]/17, /19, [special]/5, [class.ctor]/4.3, /4.4, /4.10, /4.12, [class.dtor]/5.6, [class.base.init]/2, /13.3, [class.copy]/12.2, /12.4, /18.2, /25.2, /25.4, /26.3, /27, [except.spec]/15.1.1.1, /15.2, /15.3 are all places where "non-static data members" could or should have "direct" prefixed.

On the other hand, in some places (e.g. in the amended [class], or in [class.copy]/23.2-23.3, /28) "members" is implicitly taken to include inherited members, so it's a bit of a mess.