JTC1/SC22/WG21
N0804
Accredited Standards Committee X3 Doc No: X3J16/95-0204 WG21/N0804
Information Processing Systems Date: November 3, 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
============================================
OB1- What are the requirements of the object model on object layouts?
1.6[intro.object] paragraph 1:
"An object is a region of storage and, unless it is a bit-field,
or is part of another object and has a virtual base class,
occupies one or more contiguous bytes of storage."
I believe this is not quite right.
12.7 [class.cdtor] paragraph 1 says:
"For an object of non-POD class type, before the constructor begins
execution and after the destructor finishes execution, referring
to any nonstatic member or base class of the object results in
undefined behavior."
If I remember the object model discussions correctly, the rule in
12.7 paragraph 1 was adopted because folks wanted to allow base
classes and members of non-POD classes to be allocated on the heap.
Proposed Resolution:
1.6 paragraph 1 needs to be modified as follows:
"An object is a region of storage and, unless it is a bit-field, or
is an object of non-POD class type, occupies one or more
contiguous bytes of storage."
569 - Is the mapping of members separated by access specifiers
implementation-defined or unspecified?
9.2[class.mem] paragraph 11 says:
"The order of allocation of nonstatic data members separated by an
access-specifier is implementation-defined."
Shouldn't this be: unspecfied?
Implementation-defined requires implementation to document how the
members are mapped while unspecified doesn't. (note: the same wording
appears in 11.1 para 2).
Proposed Resolution:
9.2 and 11.1 should say that this mapping is unspecified.
533 - Is an anonymous union a type?
9.5[class.union] paragraph 2 says:
"A union of the form
union { member-specification };
is called an anonymous union; it defines an unnamed object (not a
type)."
Is an anonymous union a type?
The sentence in paragraph 2 says it is an object with no type.
Doesn't this break the object model?
Is an anonymous union a class?
Do the rules in the WP that apply to classes apply to anonymous
unions as well? The WP is unclear.
Proposed Resolution:
An anonymous union is neither a type or an object.
Modify 9.5 paragraoh 2 as follows:
"A union of the form
union { member-specification };
is called an anonymous union; it is neither an object nor a
type."
529 - Can two base class subobjects be allocated at the same address?
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 a base class subobject have
zero size?
[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.]
Discussion:
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 contains a single base class B, it may have
zero size. There is no way another B can have the same address
since the complete object has a unique address.
If a complete object contains several subobjects of type B, the
compiler must ensure the subobjects have different offsets in the
complete object. This may require that some additional bytes be
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.
Proposed Resolution:
Adopt solution 2)
because I believe this 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."
589 - Can the return type of an overridding function be incomplete and
different from the return type of the base class function?
10.3[class.virtual] paragraph 5 says:
"A program is ill-formed if the return type of any overriding
function differs from the return type of the overridden function
unless the return type of the latter is pointer or reference
(possibly cv-qualified) to a class B, and the return type of the
former is pointer or reference (respectively) to a class D such
that B is an unambiguous direct or indirect base class of D,
accessible in the class of the overriding function, and the
cv-qualification in the return type of the overriding function is
less than or equal to the cv-qualification in the return type of
the overridden function."
If the return types are different and the return type of the
overridding function is imcomplete, is the program ill-formed?
Proposed Resolution:
I believe that the rule above means that by the time the derived
class overrides the base class virtual function, the return type of
the virtual functions in both the base class and the derived class
must complete class types.
Add the following text after the text from paragraph 5 listed above:
"The return type of the overriding virtual function shall be of a
complete class type."
536 - When can objects be eliminated (optimized away)?
Paragraph 15 indicates that an implementation is allowed to eliminate
an object if it is created with the copy of another.
ISSUE 1:
--------
However, this is in clear contradiction with other WP text:
3.7.1[basic.stc.static] says:
"If an object of static storage duration has initialization or a
destructor with side effects; it shall not be eliminated even if
it appears to be unused."
3.7.2[basic.stc.automatic] says:
"If a named automatic objects has initialization or a destructor
with side effects; it shall not be destroyed before the end of its
block, nor shall it be eliminated as an optimization even if
appears to be unused."
So which is right?
Many have suggested different ways to resolve this difference:
Andrew Koenig [core-5975]:
The correct way to resolve the contradiction is to say that copy
optimization applies only to local objects.
Patrick Smith [core-6083]:
1) Just weaken 3.7.1 and 3.7.2 so they can be overridden by the
copy constructor optimization.
2) Restrict the copy constructor optimization to only eliminate
temporaries representing function return values.
3) Require the programmer to explicitly mark the classes for
which the copy constructor optimization is permitted even
though it would violate 3.7.1 or 3.7.2.
4) Require the programmer to explicitly mark the classes for
which the copy constructor optimization is not permitted when
it would violate 3.7.1 or 3.7.2.
Solutions 3) and 4) require some sort of language extension to allow
users to mark classes for which this optimization should be
performed/ignored. At this stage in the game, I believe most
committee members are against the adoption of further language
extensions.
ISSUE 2:
--------
Jerry Schwarz in core-5993:
What may be of concern is not side effects in general, but resource
allocation. E.g. if Thing is intended to obtain a lock that is held
until it is destroyed, then you do indeed have to be careful about
the semantics you give to the copy constructor.
{
Thing outer ; // get the lock
{
Thing inner = outer ; // copy constructor increments
// count on lock.
// do stuff that requires the lock
inner.release() ; // decrement count
// do stuff that doesn't require the lock
}
// do stuff that still requires the lock.
}
The optimization allows outer and inner to be aliased, and the
explicit release in inner may cause the lock to be released too
early.
Is Jerry's concern worth worrying about?
Two possible resolutions were proposed:
Jerry suggested the following:
When we introduced the "explicit" keyword I remember considering
what it would mean on copy constructors and thinking about the
possibility that it would suppress this optimization.
Jason Merrill proposed in c++std-core-5978:
Perhaps the language in class.copy should be modified so that it
only applies when the end of one object's lifetime coincide with
the end of its copy's lifetime.
Proposed Resolution:
I believe the simplest solution at this point is to say that only
temporary objects can be eliminated by this optimization.
This resolution ensures that both Jerry's example and the rules
in 3.7.1 and 3.7.2 still apply.