Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>One of the "special abilities" of a standard-layout class, is that you can <code>reinterpret_cast</code> a pointer to a standard-layout class object to the type of its first data member, and thus obtain a pointer to the first data member. [Edit: 9.2/19] Further, a standard-layout class with non-static data members is permitted to have empty bases. As you no doubt know, most implementations put base class sub-objects at the start of complete sub-objects. This combination of restrictions effectively <em>mandates</em> that the empty-base-class optimization is applied to all bases of standard-layout classes.</p> <p>However, as other answers have explained, all base class sub-objects and member sub-objects that are part of the same complete object must be distinct, i.e., have different addresses if they are of the same type. Classes which violate your bullet point (that have a base class that is the same type as the first member) can't have the empty-base-class optimization fully applied, and thus can't be standard-layout classes if the base classes are positioned at the start of the complete object.</p> <p>So I'm pretty sure this is what it's getting at - it's trying to say "if a class has base classes, and the empty-base-class optimization can't be applied, then the class is not standard-layout".</p> <p>Edit: I'm being a bit slack with terminology here - it's possible to construct cases where the empty base class optimization can't be fully applied among the base classes (for example, in your <code>struct D</code>), but that doesn't matter because the base classes can still start at the beginning of the object, and conceptually "overlay" the data members, similar to a <code>union</code>. As you say, the base sub-objects get their addresses incremented if they (or a base) would otherwise overlay another base. While it's possible for the same thing to happen to bases of standard-layout cases (if they would overlap a data member of the same type), this would break existing ABIs, and add a special case for little gain.</p> <hr> <p>You're saying that this is "forbidding" a possibility - it's not really forbidding, from my point of view, it's just not granting "standard-layout" status to types that didn't have that originally anyway (classes with bases were not PODs in C++03). So it's not forbidding such types, it's just saying that they don't get the special standard-layout treatment, which they weren't guaranteed in the first place.</p> <hr> <p>Regarding my assertion that non-static data member subobjects and base subobjects are distinct, see if you find this convincing:</p> <ul> <li>5.9/2 (relational operators on pointers) makes it clear that no two data member subobjects (at least, with the same access specifier) have the same address as one another.</li> <li>5.3.1/1 (the unary operator*) says "the expression to which it is applied shall be a pointer to an object type [snip] and the result is an lvalue referring to <em>the</em> object to which the expression points." (emphasis added) This implies that there is at most one object of a given type at a particular address, at a given time.</li> <li>1.8/2 "A subobject can be a member subobject (9.2), a base class subobject (Clause 10), or an array element."... I think this implies that the categories are mutually exclusive (even if their storage overlaps). Other parts of the standard pretty strongly imply that base subobjects and member subobjects are distinct (e.g. 12.6.2).</li> <li>Steve M's citation of 10.1/4 "For each distinct occurrence of a non-virtual base class in the class lattice of the most derived class, the most derived object (1.8) shall contain a corresponding distinct base class subobject of that type." - I believe this means that different bases must be at different addresses, or else they would not be "distinct" objects - there would be no way to distinguish them during their common lifetime.</li> </ul> <p>I don't know how convincing this is, if you don't consider footnotes as normative or sufficiently indicating intention. For what it's worth, Stroustrup explains derived classes in "The C++ Programming Language" 12.2 in terms of member objects that have compiler-supported conversion from derived to base. Indeed, at the very end of this section, he explicitly says: "Using a class as a base is equivalent to declaring an (unnamed) object of that class. Consequently, a class must be defined in order to be used as a base (section 5.7)."</p> <hr> <p>Also: it seems that GCC 4.5 does <em>not</em> bump up the base class in this specific situation, even though it <em>does</em> bump up the bases where you have repeated base classes (as you showed):</p> <pre><code>#include &lt;assert.h&gt; #include &lt;iostream&gt; struct E {}; struct D: E { E x ; }; int main() { D d; std::cerr &lt;&lt; "&amp;d: " &lt;&lt; (void*)(&amp;d) &lt;&lt; "\n"; std::cerr &lt;&lt; "&amp;d.x: " &lt;&lt; (void*)(&amp;(d.x)) &lt;&lt; "\n"; std::cerr &lt;&lt; "(E*)&amp;d: " &lt;&lt; (void*)(E*)(&amp;d) &lt;&lt; "\n"; assert(reinterpret_cast&lt;E *&gt;(&amp;d) == &amp;d.x); //standard-layout requirement } </code></pre> <p>Output (Linux x86-64, GCC 4.5.0): </p> <pre> &d: 0x7fffc76c9420 &d.x: 0x7fffc76c9421 (E*)&d: 0x7fffc76c9420 testLayout: testLayout.cpp:19: int main(): Assertion `reinterpret_cast(&d) == &d.x' failed. Aborted </pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload