ISO/ IEC JTC1/SC22/WG21 N0724


Accredited Standards Committee X3       Doc No: X3J16/95-0124   WG21/N0724
Information Processing Systems          Date:    Jun 30, 1995
Operating under the procedures of       Project: Programming Language C++
American National Standards Institute   Ref Doc:
                                        Reply to: Josee Lajoie
                                                  (josee@vnet.ibm.com)
 

Object Model Issues and Proposed Resolutions
============================================
 
o object mapping
================
 
 *Issue 417:
  ----------
  Should pointer arithmetic be allowed for pointer-to an abstract class?
  Should taking the address of such a pointer, using such a pointer
  with the prefix and postfix ++/-- operators, with the built-in []
  or * operators, with the built-in additive operators +/- result in
  undefined behavior?
 
  Proposal 417:
  -------------
  No.
  This is the status quo since the draft does not say that the
  operators listed above cannot be applied to pointer to abstract
  classes.
  Should the draft be more explicit about this?
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue  11:
  ----------
  What can be guaranteed by the standard regarding the storage layout
  of base class and base class members?
 
  10.1[class.mi] paragraph 2 says:
    "The order of derivation is not significant except as specified
     by the semantics of initialization by constructor (12.6.2),
     cleanup (12.4), and storage layout (5.4, 9.2 11.1).
 
  9.2[class.mem] paragraph 11 says:
    "Nonstatic data members of a (non-union) class declared without an
     intervening access-specifier are allocated so that later members
     have higher addresses within a class object.  The order of
     allocation of nonstatic data members separated by an
     access-specifier is implementation-defined."
 
  Proposal  11a:
  -------------
  The draft should not say anything about the impact of the order of
  derivation on the storage layout.  All this is implementation specific
  and there is no carateristics that is guaranteed to be true on all
  implementations that can be imposed by the standard.
 
  10.1[class.mi] paragraph 2 should be rewritten to say:
    "The order of derivation is not significant except as specified
     by the semantics of initialization by constructor (12.6.2),
     cleanup (12.4)."
 
  Proposal  11b:
  -------------
  The draft should indicate that the rules described in 9.2[class.mem]
  paragraph 11 are also true when base class members are mapped in a
  complete object.
 
  After 10[derived] paragraph 3:
    "The order in which the base class objects are allocated in the
     complete object is unspecified."
  add:
    "However, the members of a single base class are mapped in the
     complete object according to the requirements described in 9.2;
     the nonstatic data members of the base class declared without an
     intervening access-specifier are allocated so that the later
     members have higher addresses within the complete object.  The
     order of allocation within the complete object of nonstatic data
     members of a base class separated by an access-specifier is
     unspecified."
 
  Also, 9.2 should say "unspecified" instead of
  "implementation-defined".
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue 442:
  ----------
  10.1[class.mi] paragraph 3 says:
    "A class shall not be specified as a direct base class of a derived
     class more than once but it can be an indirect base class more
     than once."
 
  This paragraph leaves the faith of the following program unclear:
 
        class A { };
        class B : public A { };
        class C : public A, public B { };
 
  Can a class have a direct and an indirect base of the same type?
 
  Proposal 442:
  -------------
  The text of the paragraph above should be extended to say that
  the program above is ill-formed.
    "A class shall not be specified as a direct base class of a derived
     class more than once, and shall not be specified both as a direct
     base class and as an indirect base class of a derived class, but it
     can be specified as an indirect base class more than once."
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue 481:
  ----------
  9[class] paragraph 3 says:
    "A class with an empty sequence of members and base class objects is
     an empty class. Objects of an empty class have a nonzero size."
 
  Some have argued that these statements do not apply to base class
  subobjects.  Can a base class subobject be of 0-size?
 
    struct B { void f(); };
    struct L : B{};
    struct R : B{};
    struct D : L,R{};
 
  Since B has no data members, can B have the same address as another
  member subobject of of D?  That is, can the base class subobject B
  within D have 0-size?
 
  Discussion 481:
  ---------------
  [note: Certain folks on the Core reflector have requested that
   the draft also be changed to allow complete objects and member
   subobjects to have 0-size. I consider this to be an extension and
   will not cover it here. I am only concerned with clarifying the
   requirements for the size of base class subobjects.]
 
  During the discussions on the core reflector, three options were
  proposed:
 
  1) No, all objects in C++ (including base class subobjects) must be
     of non 0-size.
 
  2) Yes, base class subobjects can be of non 0-size.
     However, base class subobjects that are of non 0-size must still
     respect the restrictions in 5.9[expr.rel], paragraph 2 i.e.:
 
     "Pointers to objects or functions of the same type (after pointer
      conversions) can be compared; the result depends on the relative
      positions of the pointed-to objects or functions in the address
      space as follows:
      -- If two pointers to the same type point to the same object or
         function, or both point one past the end of the same array, or
         are both null, the compare equal.
      ...
     "
     What this means is that 0-sized subobjects are allowed, but that
     two subobjects of the same class type cannot be located at the same
     address location.
 
     If a complete object of a contains a single B, it may have zero
     size.  There is no way of another B can have the same address since
     the complete object has a unique address because objects cannot
     overlap.
 
     If a complete object contains several subobjects of type B, the
     compiler should make sure they have different offsets in the
     complete object.  This may require some additional bytes being
     allocated between such objects.
 
     [Erwin Unruh in core-5430]:
       class B {};
       class L : B {};
       class R : B {};
       class D : L,R { int i,j; }
 
       Layout:
               0       base class L
               0       indirect base L::B
               0       integer i
               4       base class R
               4       indirect base R::B
               4       integer j
 
               8       end of object (or start of next one)
 
       Not a single byte is wasted and both subobjects of type B have
       different addresses.
 
  3) Yes, base class subobjects can be of non 0-size and can be located
     at the same address location.  In this case, 5.9[expr.rel]
     paragraph 2 needs to be changed to indicate that the rule requiring
     that two pointers to the same type only compare equal if they point
     to the same object does not apply to base class subobjects.
 
  Proposal 481:
  -------------
  Adopt solution 2)
  because it is the status quo.
 
  9[class] paragraph 3 needs to be changed to say:
    "A class with an empty sequence of members and base class objects
     is an empty class.  Complete objects and member subobjects of an
     empty class type shall have a nonzero size."
 
  10[derived] needs to be updated to say:
    "A base class subobject can be of zero size.  However, two
    subobjects of the same class type cannot be located at the same
    storage location."
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 
o object model
==============
 
 *Issue Box 18:
  -------------
  What is the lifetime of an arrays?
  Box 18 in 3.8[basic.life] proposes two options:
  "1) arrays always behave like PODs, their lifetime starts as soon as
      storage is allocated for the array and ends when the storage
      which the array occupies is reused or released, or
   2) arrays of POD types have a lifetime that starts as soon as
      storage is allocated for the array and ends when the storage
      which the array occupies is reused or released; arrays of classes
      with non-trivial constructors have a lifetime that starts when the
      constructor call for the last element has completed; arrays of
      classes with non-trivial destructors have a lifetime that ends
      when the destructor call for the first array element starts.
  "
 
  Arrays are like aggregate classes.
 
  Proposal Box 18:
  ----------------
  Adopt solution 1).
 
  An aggregate is a collection of subobjects.  I believe it makes more
  sense to say that the lifetime of the aggregate is the lifetime of the
  storage in which it resides and let the lifetime of each individual
  subobject determine what can be done with the subobject.
 
  For example:
    struct A { ~A(); };
    A a[5];
    a[0].A~();
 
    The first element of a has been destroyed.  While the aggregate and
    the other elements of the array are still considered objects and can
    therefore still be manipulated as objects, a[0] is not an object
    anymore and manipulating a[0] must obey the restrictions described
    in 3.8[basic.life].
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue Box 19:
  -------------
  Can the storage in which a const object resides be reused?
  And if so, how is the "write-protected" attribute that may be
  associated with the memory in which the object resides interpreted?
 
  Proposal Box 19:
  ----------------
  Add the resolution in box 19 as paragraph 9 of 3.8[basic.life].
    "Because the storage which a const object occupies may be
     write-protected, creating a new object at the storage location
     which a const object occupies or at the storage location which a
     const object used to occupy before its lifetime ended results in
     undefined behavior.  [Example:
        struct B {
           ~B();
        };
        void h() {
           const B b;
           b.~B();
           new (&b) B; // undefined behavior
        }
    --end of example]
    "
 
  Implementations can put some const objects with constructors and
  destructors in write-protected memory if they can figure out that the
  constructor and destructor do not modify the objects.  Therefore, it
  is not possible to assume that it is the destructor's responsability
  to render the memory "writeable".
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue OM1:
  ----------
 
  9.6[class.union] says:
    "A union can be thought of as a class whose member objects all begin
     at offset zero and whose size is sufficient to contain any of its
     member objects. At most one of the member objects can be stored
     in a union at any time."
 
  This wording needs to be reworked to take into account the object
  model described in 3.8[basic.life]. That is, only one object will be
  alive at any one time in a union.
 
  Proposal OM1:
  -------------
 
    "A union is a class for which any member object is located at
     offset zero and whose size is sufficient to contain its largest
     member.  A union can represent at most one member object at any
     given time."
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue OM2:
  ----------
  12.7[class.cdtor] paragraph 2 does not cover conversions
  from lvalues of derived classes to lvalues of base class types.
 
  Proposal OM2:
  -------------
  Change 12.7[class.cdtor] paragraph 2 as follows:
    "To explicitly or implicitly convert a pointer to
     __(or an lvalue of)__
     an object of class X to a pointer to
     _(or a reference to)_
     a direct or indirect base class B, the construction of X and the
     construction of all of its direct or indirect bases that directly
     on indirectly derive from B shall have started and the destruction
     of these classes shall not have completed, otherwise the
     computation
     _(or the reference)_
     results in undefined behavior.  To form a pointer to
     _(or a reference to)_
     a direct nonstatic member of an object X given a pointer to X
     _(or an lvalue of type X)_
     the construction of X shall have started and the destruction of X
     shall not have completed, otherwise the computation
     _(or the reference)_
     results in undefined behavior.
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue OM3:
  ----------
  12.7[class.cdtor] paragraph 3 is not clear in saying that the rules
  in this paragraph only describe virtual function calls _on the object
  under construction_.
 
  Proposal OM3:
  -------------
  Change 12.7[class.cdtor] paragraph 3 as follows:
  "Member functions, including virtual functions (_class.virtual_),
   can be called during construction or destruction (_class.base.init_).
   When a virtual function is called directly or indirectly from a
   constructor (including from its ctor-initializer) or from a
   destructor
   _and the object-expression refers to the object under construction
   or destruction_,
   the function called is the one defined in the constructor or
   destructor's own class or in one of its bases, but not a function
   overriding it in a class derived from the constructor or destructor's
   class or overriding it in one of the other base classes of the
   complete object."
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 
o Terminology
=============
 
 *Issue 421:
  ----------
 
 ===============================================================================
 Chapter 1 - Introduction
--------------------------
Work Group:     Core
Issue Number:   421
Title:          What is a complete object? a sub-object?
Section:        1.6 [intro.object] Object Model
Status:         active
Description:
        There appears to have been a substantive change in the definition of
        "sub-object" and "complete object" in the Working Paper.
 
        Sub-objects used to include only objects representing base classes.  A
        complete object used to include all objects (even members) that aren't
        base class objects of other objects.  Now sub-objects include members,
        and complete objects exclude members.  This introduces a number of
        unfortunate side-effects in the standard where the definitions are used.
        3.8 [basic.life] p7:
        "-- the original object was a complete object of type T and the new
         object is a complete object of type T (that is, they are not base
         class subobjects)."
 
        5.2.6 [expr.dynamic.cast] p7:
        "If T is ``pointer to cv void'', then the result is a pointer to the
         complete object pointed to by v. ...
 
         If, in the complete object pointed (referred) to by v, v points
         (refers) to an public base class sub-object of a T object, ...
         Otherwise, if the type of the complete object has an unambiguous
         public base class of type T, the result is a pointer (reference) to
         the T sub-object of the complete object."
 
        5.2.7 [expr.typeid] p3
        "If the expression is a reference to a polymorphic type, the type_info
         for the complete object referred to is the result. ...
 
         ...  Otherwise, the result of the typeid expression is the
         value that represents the type of the complete object to which
         the pointer points."
 
        10 [derived] p3
        "3 The order in which the base class subobjects are allocated in the
           complete object is unspecified."
 
         5 A base class subobject might have a layout different from the layout
           of a complete object of the same type.  A base class subobject might
           have a polymorphic behavior of a complete object of the same type."
 
        10.1 [class.mi] p4
        "For each distinct occurrence of a nonvirtual base class in the class
         lattice of the most derived class, the complete object shall contain a
         corresponding distinct base class subobject of that type.  For each
         distinct base class that is specified virtual, the complete object
         shall contain a single base class subobject of that type."
 
        12.7 [class.cdtor] p3:
        "3 When a virtual function is called directly or indirectly from a
           constructor (including from its ctor-initializer ) or from a
           destructor, the function called is the one defined in the constructor
           or destructor's own class or in one of its bases, but not a function
           overriding it in a class derived from the constructor or destructor's
           class or overriding it in one of the other base classes of the
           complete object."
           ...
         5 When a dynamic_cast is used in a constructor (including in its
           ctor-initializer) or in a destructor, or used in a function called
           (directly or indirectly) from a constructor or destructor, if the
           operand of the dynamic_cast refers to the object under construction
           or destruction, this object is considered to be a complete object
           that has the type of the constructor or destructor's class.
Resolution:
  Proposal 421:
  -------------
        We need to introduce another term in the WP to describe objects that
        are either member subobject or complete objects.
        It was suggested before that the term "nonbase" object could be used.
Requestor:      Neal M Gafter <gafter@mri.com>
Owner:          Josee Lajoie (Object Model)
Emails:         edit-195, edit-196
Papers:
 
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 
 *Issue 460:
  ----------
  A definition for the term "variable" is needed in 3[basic] paragraph 4.
 
  Proposal 460:
  -------------
  "A variable is introduced by an object's declaration and the
   variable's name denotes the object."