ISO/IEC JTC1 SC22 WG21 N2762 = 08-0272 - 2008-09-18
Lawrence Crowl, [email protected], [email protected]
Jens Maurer, [email protected]
William Michael Miller
This document is a revision of N2749 = 08-0259 - 2008-08-24. It merges in core issue 509 "Dead code in the specification of default initialization". The issue 509 edits removes duplicate wording and merges it into the definition of "default initialization".
The original formulation of deleted functions (N2210) defined deleted special member functions to be trivial. The intent was to not change the efficiency features associated with trivial types simply because use of a function is prohibited. However, the adopted formulation (N2346) made deleted special member functions non-trivial.
The consequence of non-trivial deleted functions
is that many desirable efficiency features defined for trivial types
no longer apply.
For example, deleting a default constructor made a class non-trivial,
which in turn no longer guaranteed static initialization.
Indeed, there is no reason that a non-deleted, non-trivial default constructor
should necessarily affect static initialization either.
The problem extends to other features,
including object lifetime, parameter passing, and memcpy
.
The core of the problem is that many features are defined for trivial types when they generally rely on only a subset of the attributes that define a trivial type.
We propose to redefine those features that are defined with trivial types to be defined with the set of attributes crucial to the feature. Features already defined directly in terms of attributes need no change. This proposal is a continuation in the work of decomposing POD types and their features that began with N2342 POD's Revisited; Resolving Core Issue 568 (Revision 5).
In our solution, we introduce the notion of a trivially copyable type. We then apply this notion in several places. We have chosen a conservative definition for trivally copyable; at lease one weaker form exists.
With the introduction of trivally copyable types, and the redefinition of features on type attributes, there are very few remaining uses of trivial types in the standard. We recommend that any new uses be very carefully considered and weaker types are likely to be preferable.
Several core-language issues are closely related. A full resolution of the problems requires resolving these issues as well. These issues are resolved by N2757 or this paper.
We note that the term "trivial default constructor" (12.1p5) used to imply "no user-declared constructor at all" (other than the copy constructor), because otherwise the default constructor would not have been implicitly declared. And not being implicitly declared, must necessarily have been user-defined and hence not trivial. However, with the arrival of defaulted functions that retain their triviality, this is no longer the case. Singling out default constructors as "trivial" and assigning special semantics seems non-intuitive given that any number of other constructors may be defined and used to initialize an object.
In summary, we propose the following changes to the standard.
memcpy
provisions and value representations
are only concerned with copying and assignment of objects,
but not with the issue how an object obtained its initial value.
The presence or absence of user-defined constructors
will not make a difference.
qsort
should only be concerned
with copying and potentially destruction, not initial values.
So limiting the realm of undefined behavior in this case
should not be a problem.
Edit paragraph 5 as follows.
An object of
trivialtrivially copyable or standard-layout type (3.9) shall occupy contiguous bytes of storage.
Edit paragraph 2 as follows.
[Note: the lifetime of an array object
or of an object of trivial type (3.9)starts as soon as storage with proper size and alignment is obtained, and its lifetime ends when the storage which the arrayor objectoccupies is reused or released. 12.6.2 describes the lifetime of base and member subobjects. —end note]
Edit paragraph 5 as follows.
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated35) [Footnote: For example, before the construction of a global object of non-POD class type (12.7). —end footnote] or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. Such a pointer refers to allocated storage (3.7.3.2), and using the pointer as if the pointer were of type
void*
, is well-defined. Such a pointer may be dereferenced but the resulting lvalue may only be used in limited ways, as described below.If the object will be or was of a class type with a non-trivial destructor, and the pointer is used as the operand of a delete-expression, the program has undefined behavior.If the object will be or was of a non-trivial class type, theThe program has undefined behavior if:
- the object will be or was of a class type with a non-trivial destructor, and the pointer is used as the operand of a delete-expression,
- the pointer is used to access a non-static data member or call a non-static member function of the object, or
- the pointer is implicitly converted (4.10) to a pointer to a base class type, or
- the pointer is used as the operand of a
static_cast
(5.2.9) (except when the conversion is tovoid*
, or tovoid*
and subsequently tochar*
, orunsigned char*
).- the pointer is used as the operand of a
dynamic_cast
(5.2.7) [Example: .... end example]
Edit paragraph 6 as follows.
Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any lvalue which refers to the original object may be used but only in limited ways. Such an lvalue refers to allocated storage (3.7.3.2), and using the properties of the lvalue which do not depend on its value is well-defined.
If an lvalue-to-rvalue conversion (4.1) is applied to such an lvalue, the program has undefined behavior;if the original object will be or was of a non-trivial class type, theThe program has undefined behavior if:
- an lvalue-to-rvalue conversion (4.1) is applied to such an lvalue,
- the lvalue is used to access a non-static data member or call a non-static member function of the object, or
- the lvalue is implicitly converted (4.10) to a reference to a base class type, or
- the lvalue is used as the operand of a
static_cast
(5.2.9) except when the conversion is ultimately to cvchar&
or cvunsigned char&
, or- the lvalue is used as the operand of a
dynamic_cast
(5.2.7) or as the operand of typeid.
Edit paragraph 2 as follows:
For any object (other than a base-class subobject) of
trivialtrivially copyable typeT
, whether or not the object holds a valid value of typeT
, the underlying bytes (1.7) making up the object can be copied into an array of char or unsigned char.36) If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value.
Edit paragraph 3 as follows:
For any
trivialtrivially copyable typeT
, if two pointers toT
point to distinctT
objectsobj1
andobj2
, where neitherobj1
norobj2
is a base-class subobject, if the value ofobj1
is copied intoobj2
, using thestd::memcpy
library function,obj2
shall subsequently hold the same value asobj1
. [Example:T* t1p;
T* t2p;
// provided thatt2p
points to an initialized object ...std::memcpy(t1p, t2p, sizeof(T));
// at this point, every subobject oftrivialtrivially copyable type in*t1p
contains // the same value as the corresponding subobject in*t2p
—end example]
Edit paragraph 4 as follows:
The object representation of an object of type
T
is the sequence ofN unsigned char
objects taken up by the object of typeT
, whereN
equalssizeof(T)
. The value representation of an object is the set of bits that hold the value of typeT
. Fortrivialtrivially copyable types, the value representation is a set of bits in the object representation that determines a value, which is one discrete element of an implementation-defined set of values. [Footnote: The intent is that the memory model of C++ is compatible with that of ISO/IEC 9899 Programming Language C. —end footnote]
Edit paragraph 9 as follows.
Arithmetic types (3.9.1), enumeration types, pointer types, pointer to member types (3.9.2), and
std::nullptr_t
, and cv-qualified versions of these types (3.9.3) are collectively called scalar types. Scalar types, POD classes (clause 9), arrays of such types and cv-qualified versions of these types (3.9.3) are collectively called POD types. Scalar types, trivially copyable class types (clause 9), arrays of such types and cv-qualified versions of these types (3.9.3) are collectively called trivially copyable types. Scalar types, trivial class types (clause 9), arrays of such types and cv-qualified versions of these types (3.9.3) are collectively called trivial types. Scalar types, standard-layout class types (clause 9), arrays of such types and cv-qualified versions of these types (3.9.3) are collectively called standard-layout types.
Edit paragraph 7 as follows.
... Passing an argument of
non-trivialclass type (clause 9) with a non-trivial copy constructor or a non-trivial destructor with no corresponding parameter is conditionally-supported, with implementation-defined semantics. ...
Edit paragraph 16 as follows. This edit arises from issue 509.
A new-expression that creates an object of type
T
initializes that object as follows:
- If the new-initializer is omitted
:, the object is default-initialized (8.5 dcl.init); if no initialization is performed, the object has indeterminate value.
IfT
is a (possibly cv-qualified) non-trivial class type (or array thereof), the object is default-initialized (8.5).IfT
is a const-qualified type, the underlying class type shall have a user-provided default constructor.Otherwise, the object created has indeterminate value. IfT
is a const-qualified type, or a (possibly cv-qualified) trivial class type (or array thereof) containing (directly or indirectly) a member of const-qualified type, the program is ill-formed;- ...
Edit paragraph 2 as follows. Note that issue 684 will make non-conflicting edits to this paragraph.
A conditional-expression is a constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional (5.16) operations that are not evaluated are not considered [Note: an overloaded operator invokes a function —end note]:
- ...
- a class member access (5.2.5) unless its postfix-expression is of
trivial orliteral type or of pointer totrivial orliteral type;- ...
Edit paragraph 3 as follows:
It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps78) from a point where a local variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has
trivial type (3.9)scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer (8.5).
Edit paragraph 5 as follows, splitting paragraph 5 into three paragraphs, one for each kind of initialization. The majority of this edit arises from issue 509.
To zero-initialize an object or reference of type
T
means:
if T is std::nullptr_t, the object is set to the value of nullptr;otherwise,ifT
is a scalar type (3.9), the object is set to the value0
(zero), taken as an integral constant expression, converted toT
; [Footnote: As specified in 4.10, converting an integral constant expression whose value is0
to a pointer type results in a null pointer value. —end footnote]- if
T
is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized;- if
T
is a (possibly cv-qualified) union type, the object's first non-static named data member[Footnote: This member must not be static, by virtue of the requirements in 9.5. —end footnote]is zero-initialized;- if
T
is an array type, each element is zero-initialized;- if
T
is a reference type, no initialization is performed.
To default-initialize an object of type
T
means:
- if
T
is a (possibly cv-qualified)non-trivialclass type (clause 9), the default constructor forT
is called (and the initialization is ill-formed ifT
has no accessible default constructor);- if
T
is an array type, each element is default-initialized;- otherwise,
the object is zero-initializedno initialization is performed.If a program calls for the default initialization of an object of a const-qualified type
T
,T
shall be a class type with a user-provided default constructor.
To value-initialize an object of type
T
means:
- if
T
is a (possibly cv-qualified) class type (clause 9) with a user-provided constructor (12.1), then the default constructor forT
is called (and the initialization is ill-formed ifT
has no accessible default constructor);- if
T
is a (possibly cv-qualified) non-union class type without a user-provided constructor, thenevery non-static data member and base-class component ofthe object is zero-initialized and, ifT
is value-initialized; [Footnote: Value-initialization for such a class object may be implemented by zero-initializing the object and then calling the default constructor. —end footnote]T
's implicitly-declared default constructor is non-trivial, that constructor is called.- if
T
is an array type, then each element is value-initialized;- otherwise, the object is zero-initialized.
Edit paragraph 6 as follows, This edit arises from issue 509.
A program that calls for default-initialization or value-initialization of an entity of reference type is ill-formed.
IfT
is a cv-qualified type, the cv-unqualified version ofT
is used for these definitions of zero-initialization, default-initialization, and value-initialization.
Edit paragraph 7 as follows, This edit arises from issue 509.
[Note: Every object of static storage duration shall be zero-initialized at program startup before any other initialization takes place.
[Note: inIn some cases, additional initialization is done later. —end note]
Edit paragraph 9 as follows. The majority of this edit arises from issue 509.
If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, a non-static object has indeterminate value. [Note: Objects with static storage duration are zero-initialized, see 3.6.2 basic.start.init. —end note]
and the object is of (possibly cv-qualified) non-trivial class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-provided default constructor. Otherwise, if no initializer is specified for a non-static object, the object and its subobjects, if any, have an indeterminate initial value95) [Footnote: This does not apply to aggregate objects with automatic storage duration initialized with an incomplete brace-enclosed initializer-list; see 8.5.1 dcl.init.aggr. —end footnote]; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.
Edit paragraph 5 as follows. Note that this edit is subsequent to the application of the edits in core issue 683 in N2757.
A trivially copyable class is a class that:
- has no non-trivial copy constructors (12.8),
- has no non-trivial copy assignment operators (13.5.3, 12.8), and
- has a trivial destructor (12.4).
A trivial class is a class that:
- has a trivial default constructor (12.1), and
has no non-trivial copy constructors (12.8),has no non-trivial copy assignment operators (13.5.3, 12.8), andhas a trivial destructor (12.4).- is trivially copyable.
[Note: in particular, a trivially copyable or trivial class does not have virtual functions or virtual base classes. —end note]
Within paragraph 1 edit as follows.
... [Note: If any non-static data member of a union has a non-trivial default constructor (12.1), copy constructor (12.8), copy assignment operator (12.8), or destructor (12.4), the corresponding member function of the union must be
user-declareduser-provided or it will be implicitly deleted (8.4) for the union. —end note]
Edit paragraph 3 as follows.
If a class has no user-declared destructor, a destructor is declared implicitly. An implicitly-declared destructor is an
inline public
member of its class. If the class is a union-like class that has a variant member with a non-trivial destructor, an implicitly-declared destructor is defined as deleted (8.4). A destructor is trivial if it is not user-provided and if:
- the destructor is not
virtual
,- all of the direct base classes of its class have trivial destructors, and
- for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.
Edit paragraph 4 as follows, This edit arises from issue 509.
If a given non-static data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then the entity is default-initialized (8.5 dcl.init), unless the entity is a variant member (9.5 class.union), in which case no initialization is performed.
If the entity is a non-static non-variant data member of (possibly cv-qualified) class type (or array thereof) or a base class, and the entity class is a non-trivial class, the entity is default-initialized (8.5). If the entity is a non-static data member of a const-qualified type, the entity class shall have a user-provided default constructor.Otherwise, the entity is not initialized. If the entity is of const-qualified type or reference type, or of a (possibly cv-qualified) trivial class type (or array thereof) containing (directly or indirectly) a member of a const-qualified type, the program is ill-formed.After the call to a constructor for class
X
has completed, if a member ofX
is neither specified in the constructor's mem-initializers, nordefault-initialized, nor value-initializedinitialized, nor given a value during execution of the compound-statement of the body of the constructor, the member has indeterminate value.
Edit paragraph 1 as follows.
For an object of non-trivial class type (clause 9), before the constructor begins execution and after the destructor finishes execution, referring to any non-static member or base class of the object results in undefined behavior.For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior. For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.