ISO/IEC JTC1 SC22 WG21 N2749 = 08-0259 - 2008-08-24
Lawrence Crowl, Lawrence@Crowl.org, crowl@google.com
Jens Maurer, Jens.Maurer@gmx.net
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. That work is in progress.
The wording in this paper includes unmodified paragraphs. These paragraphs serve to provide context for the changes proposed. We anticipate that a revision for formal voting will remove those unmodified paragraphs.
Edit paragraph 5 as follows.
An object of
trivialtrivially copyable or standard-layout type (3.9) shall occupy contiguous bytes of storage.
Paragraph 1 will be modified by issue 688 to avoid the term "trivial". Issue 684 may also affect this paragraph.
Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. A reference with static storage duration and an object of trivial or literal type with static storage duration can be initialized with a constant expression (5.19 [expr.const]); this is called constant initialization. ...
Leave paragraph 1 unchanged.
The lifetime of an object is a runtime property of the object. The lifetime of an object of type
T
begins when:
- storage with the proper alignment and size for type
T
is obtained, and- if
T
is a class type and the constructor invoked to create the object is non-trivial (12.1), the principal constructor call (12.6.2) has completed. [Note: the initialization can be performed by a constructor call or, in the case of an aggregate with an implicitly-declared non-trivial default constructor, an aggregate initialization 8.5.1. —end note]The lifetime of an object of type T ends when:
- if
T
is a class type with a non-trivial destructor (12.4), the destructor call starts, or- the storage which the object occupies is reused or released.
Edit paragraph 2 as follows.
[Note: the lifetime of an array object or of an object of
trivial type (3.9)scalar type or of class type with a trivial default constructor starts as soon as storage with proper size and alignment is obtained, and its. The lifetime of an array object or of an object of scalar type or of class type with a trivial destructor ends when the storage which the array or object occupies is reused or released. 12.6.2 describes the lifetime of base and member subobjects. —end note]
Leave paragraph 4 unchanged.
A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (5.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
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) 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 anon-trivialclass type with virtual bases or virtual member functions, the program has undefined behavior if:
- 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*
).- ....
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-trivialclass type with virtual bases or virtual member functions, the program has undefined behavior if:
- 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.
Leave paragraph 8 unchanged.
If a program ends the lifetime of an object of type
T
with static (3.7.1) or automatic (3.7.2) storage duration and ifT
has a non-trivial destructor,36) the program must ensure that an object of the original type occupies that same storage location when the implicit destructor call takes place; otherwise the behavior of the program is undefined. This is true even if the block is exited with an exception.
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.
Leave paragraph 10 unchanged.
A type is a literal type if it is:
- a scalar type; or
- a class type (clause 9) with
- a trivial copy constructor,
- a trivial destructor,
- a trivial default constructor or at least one constexpr constructor other than the copy constructor, and
- all non-static data members and base classes of literal types; or
- an array of literal type.
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. ...
Paragraph 16 will be modified by issue 509 to avoid the term "trivial".
A new-expression that creates an object of type
T
initializes that object as follows:
- If the new-initializer is omitted:
- If
T
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. If
T
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;- ...
Leave paragraph 5 unchanged.
If the object being deleted has incomplete class type at the point of deletion and the complete class has a non-trivial destructor or a deallocation function, the behavior is undefined.
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)a trivial default constructor and a trivial destructor and is declared without an initializer (8.5).
Paragraph 4 will be modified by issue 688 to avoid the term "trivial".
The zero-initialization (8.5) of all local objects with static storage duration (3.7.1) is performed before any other initialization takes place. A local object of trivial or literal type (3.9 [basic.types]) with static storage duration initialized with constant-expressions is initialized before its block is first entered An implementation is permitted to perform early initialization of other local objects with static storage duration under the same conditions that an implementation is permitted to statically initialize an object with static storage duration in namespace scope (3.6.2). Otherwise such an object is initialized the first time control passes through its declaration; such an object is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control re-enters the declaration (recursively) while the object is being initialized, the behavior is undefined.
constexpr
specifier [dcl.constexpr]Leave paragraph 4 unchanged.
A trivial copy constructor is also a constexpr constructor.
Paragraph 7 will be modified by issue 688.
A
constexpr
specifier used in an object declaration declares the object asconst
. Such an object shall be initialized, and every expression that appears in its initializer (8.5 [dcl.init]) shall be a constant expression. Every implicit conversion used in converting the initializer expressions and every constructor call used for the initialization shall be one of those allowed in a constant expression (5.19 [expr.const]) ...
Leave paragraph 9 unchanged.
... A special member function is user-provided if it is user-declared and not explicitly defaulted on its first declaration. ...
struct trivial { trivial() = default; trivial(const trivial&) = default; trivial& operator =(const trivial&) = default; ~trivial() = default; };
...
Edit paragraph 5 as follows. This paragraph will also be modified by issue 509.
To default-initialize an object of type
T
means:
- if
T
is anon-trivialclass type (clause 9), the default constructor forT
is called (and the initialization is ill-formed ifT
has no accessible default constructor);
Edit paragraph 9 as follows. This paragraph will also be modified by issue 509.
If no initializer is specified for an object, and the object
is of (possibly cv-qualified) non-trivial class type (or array thereof)has a non-trivial default constructor, 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); if the object or any of its subobjects are of const-qualified type, the program is ill-formed.
Paragraph 14 will be heavily modified by issue 688 and thus avoid the term "trivial".
When an aggregate with static storage duration is initialized with a brace-enclosed initializer-list, if all the member initializer expressions are constant expressions, and the aggregate is a trivial type, the initialization shall be done during the static phase of initialization (3.6.2 [basic.start.init]); otherwise, it is unspecified whether the initialization of members with constant expressions takes place during the static phase or during the dynamic phase of initialization.
Edit paragraph 5 as follows.
A trivially copyable class is a class that:
- has a trivial copy constructor (12.8),
- has a trivial copy assignment operator (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 a trivial copy constructor (12.8),has a trivial copy assignment operator (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]
Leave paragraph 9 unchanged.
A POD struct is a class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). Similarly, a POD union is a union that is both a trivial class and a standard layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). A POD class is a class that is either a POD struct or a POD union.
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]
Leave paragraph 5 unchanged.
A default constructor for a class
X
is a constructor of classX
that can be called without an argument. If there is no user-declared constructor for classX
, a default constructor having no parameters is implicitly declared. An implicitly-declared default constructor is aninline public
member of its class. For a union-like class that has a variant member with a non-trivial default constructor, an implicitly-declared default constructor is defined as deleted (8.4). A default constructor is trivial if it is not user-provided (8.4) and if:
- its class has no virtual functions (10.3) and no virtual base classes (10.1), and
- all the direct base classes of its class have trivial default constructors, and
- for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.
Leave paragraph 3 unchanged.
When an implementation introduces a temporary object of a class that has a non-trivial constructor (12.1, 12.8), it shall ensure that a constructor is called for the temporary object. Similarly, the destructor shall be called for a temporary with a non-trivial destructor (12.4). Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created. This is true even if that evaluation ends in throwing an exception. The value computations and side effects of destroying a temporary object are associated only with the full-expression, not with any specific subexpression.
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.
Paragraph 4 will be modified by issue 509 to avoid use of "trivial".
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
- 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.
an implicitly-declared copy constructor is defined as deleted (8.4).
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 constructor begins 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.
Leave paragraph 4 unchanged.
If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class is a union-like class that has a variant member with a non-trivial copy constructor, an implicitly-declared copy constructor is defined as deleted (8.4). Thus, for the class definition ...
Leave paragraph 6 unchanged.
A copy constructor for class
X
is trivial if it is not user-provided (8.4) and if
- class
X
has no virtual functions (10.3) and no virtual base classes (10.1), and- each direct base class of
X
has a trivial copy constructor, and- for all the non-static data members of
X
that are of class type (or array thereof), each such class type has a trivial copy constructor;otherwise the copy constructor is non-trivial.
Leave paragraph 10 unchanged.
If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class is a union-like class that has a variant member with a non-trivial copy assignment operator, an implicitly-declared copy assignment operator is defined as deleted (8.4). The implicitly-declared copy assignment operator for a class
X
will have the form ...
Leave paragraph 11 unchanged.
A copy assignment operator for class
X
is trivial if it is not user-provided and if
- class
X
has no virtual functions (10.3) and no virtual base classes (10.1), and- each direct base class of
X
has a trivial copy assignment operator, and- for all the non-static data members of
X
that are of class type (or array thereof), each such class type has a trivial copy assignment operator;otherwise the copy assignment operator is non-trivial.
<type_traits>
synopsis [meta.type.synop]
The is_trivial
and has_trivial_
* traits
are unchanged.
reference_closure
[func.referenceclosure]Leave paragraph 3 unchanged.
The instances of
reference_closure
class template are trivial and standard-layout classes (3.9).
Leave paragraph 4 unchanged.
It makes qsort
undefined if the type is not trivial.
Chapter 29 was the original motivation for this propsal. With the proposed changes in the core language, no change is needed in this chapter.