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."