ISO/IEC JTC1 SC22 WG21 N3044 = 10-0034 - 2010-02-14
Bjarne Stroustrup
Lawrence Crowl
This paper revises N2987 = 09-0177 - 2009-11-08. The changes include a minor wording nit, removing the inheritance of explicitness in constructors (now core issue 992) and making the default definition of the move assignment operator ill-formed if the class has virtual bases.
This paper has editing conflicts with core issue 667.
Introduction
Findings
Proposal
Wording
1.8 The C++ object model [intro.object]
3.1 Declarations and definitions [basic.def]
3.2 One definition rule [basic.def.odr]
3.7.1 Static storage duration [basic.stc.static]
3.7.3 Automatic storage duration [basic.stc.auto]
3.7.4.3 Safely-derived pointers [basic.stc.dynamic.safety]
3.8 Object lifetime [basic.life]
3.9 Types [basic.types]
5.1.2 Lambda expressions [expr.prim.lambda]
5.17 Assignment and compound assignment operators [expr.ass]
6.6.3 The return statement [stmt.return]
7.1.5 The constexpr specifier [dcl.constexpr]
7.3.3 The using declaration [namespace.udecl]
8.4 Function definitions [dcl.fct.def]
8.4.1 Explicitly-defaulted definitions [dcl.fct.def.default]
8.4.2 Deleted definitions [dcl.fct.def.delete]
8.5 Initializers [dcl.init]
9 Classes [class]
9.5 Unions [class.union]
12 Special member functions [special]
12.2 Temporary objects [class.temporary]
12.3.1 Conversion by constructor [class.conv.ctor]
12.6.1 Explicit initialization [class.expl.init]
12.8 Copying and moving class objects [class.copy]
12.9 Inheriting Constructors [class.inhctor]
13.3.3.1 Implicit conversion sequences [over.best.ics]
13.3.3.1.2 User-defined conversion sequences [over.ics.user]
13.5.3 Assignment [over.ass]
15.1 Throwing an exception [except.throw]
15.4 Exception specifications [except.spec]
17.5.1.4 Detailed Specifications [structure.specifications]
17.5.2.2 Functions within classes [functions.within.classes]
18.7.2.1 type_index overview [type.index.overview]
18.8.5 Exception Propagation [propagation]
20.1.1 Template argument requirements [utility.arg.requirements]
20.6.2 Header <type_traits> synopsis [meta.type.synop]
20.6.4.3 Type properties [meta.unary.prop]
20.7.5 Class template reference_wrapper [refwrap]
20.7.12.1.4 Placeholders [func.bind.place]
20.8.13.2 Class template shared_ptr [util.smartptr.shared]
20.8.13.3 Class template weak_ptr [util.smartptr.weak]
Acknowledgments
Paper N2904 examined the generation of default copy and move operations when elements are combined into an aggregate. The proposal was discussed extensively in the July 2009 meeting, and several changes were suggested at that meeting. In addition, several changes surfaced in analysis after the meeting. This paper is the formal proposal to make move functions special.
Rather than repeat much of the arguments and analysis of N2904, we present a summary of our findings and our proposal, and then present the formal wording.
More restrictive rules for when to generate a copy would break too much existing code. The rules explored, but which no longer appear viable include:
Not generating a copy because of a pointer member.
Not generating a copy because of a user-defined destructor.
Not generating a copy when there is slicing. We cannot technically alter behavior on slicing because the slicing may occur in one translation unit but not in another.
The move operations match a subset of the expressions of the copy operations. That is, all expressions that match a move operation would match a copy operation in the absence of that move operation. So,
Adding an implicit move operation has the potential to inject new semantics into existing code, particularly PODs.
Having unnecessarily different rules for when to generate moves and copies would cause surprising behavior.
Implicitly generating a move when the copy was deleted would break much of the existing C++0x library and would leave a trap for programmers. Programmers can add the move back in with an explicit default.
Implicitly generating a move when the copy was user-defined would bypass the programmers intent for existing code, and would do so silently.
One suggestion in N2904, was to permit compiler to null pointers that are moved from. The idea was that doing so would help debugging of move semantics. However, doing so would break too much existing code, particularly for PODS.
The standard uses the concept of copying extensively. In most cases, the standard is adequately changed by replacing "copy" with "copy/move".
Built-in types have a move equivalent to their copy.
A struct copy is well-formed if and only if all its bases and members have a (usable) copy.
A struct move is well-formed if and only if all its bases and members have a (usable) move.
Moves and copies can be both =default
and =delete
,
even when not implicitly declared.
They must be well-formed, though.
A struct move is implicitly declared if it is well-formed and the corresponding copy is not user-declared. So, explicitly defaulting a copy constructor will supress the move constructor. One can add the move constructor back by explicitly defaulting it. Doing otherwise would require deleting the move, which leaves a hole in the expressions.
struct no_move { no_move( const no_move& ) = default; }; struct has_move { has_move( const has_move& ) = default; has_move( has_move&& ) = default; };
Likewise, the implicit copy operation is supressed if there is a corresponding user-declared move operation.
struct no_copy { no_copy( no_copy&& ) = default; }; struct has_copy { has_copy( const has_copy& ) = default; has_copy( has_copy&& ) = default; };
Unions have implicit deleted moves if a members has a
non-trivial moves. This rule is the same as for copy, and
means that a user-defined union copy will not shadow a move.
That is S a(x+y)
would be ill-formed.
For example,
given two classes non_trival_copier and non_trivial_mover,
that are otherwise trivial, the following happens.
union wrap_copy { non_trival_copier c; }; // implicit wrap_copy::wrap_copy( const wrap_copy& ) = delete; // declaration "wrap_copy x(y)" is ill-formed // because of delete; // declaration "wrap_copy x(f());" is well-formed // because the trival move is a better match. union wrap_move { non_trivial_mover m; }; // implicit wrap_move::wrap_move( wrap_move&& ) = delete; // declaration "wrap_move x(f());" is ill-formed // because of delete; // declaration "wrap_move x(y);" is well-formed // because the trival copy is a better match. union wrap_both { non_trival_copier c; non_trivial_mover m; }; // implicit wrap_both::wrap_both( const wrap_both& ) = delete; // implicit wrap_both::wrap_both( wrap_both&& ) = delete; // declaration "wrap_both x(y)" is ill-formed because of delete; // declaration "wrap_both x(f());" is ill-formed because of delete;
The current copy operations may throw. If we added non-throwing implicit moves, then the throw() specification would require us to put the catch for unexpected into the generated move. That catch and its control flow inhibit the optimizer and add more code. We already have people looking for ways to turn off that catch unexpected, so adding one implicitly is likely to cause some concern.
However, throwing moves are problematic within the library,
and some libary specifications prohibit throwing moves.
To enable conditional definition of move operations within the library,
we add an is_nothrow_constructible
variadic type trait.
We also extend existing type traits for copy to move.
Finally, we redefine the library concepts for copy and move to reflect the status of change in core language.
The proposed wording is usually relative to working draft N2857. However, some wording is in sections heavily affected by the decision to defer concepts, and those sections are relative to working draft N2723. Those sections are so marked.
Edit paragraph 5 as follows.
Unless it is a bit-field (9.6), a most derived object shall have a non-zero size and shall occupy one or more bytes of storage. Base class subobjects may have zero size. An object of trivially copyable, trivially movable, or standard-layout type (3.9) shall occupy contiguous bytes of storage.
Edit paragraph 3 as follows.
[Note: in some circumstances, C++ implementations implicitly define the default constructor (12.1), copy constructor (12.8), move constructor ([class.copy]), copy assignment operator (12.8), move assignment operator ([class.copy]), or destructor (12.4) member functions. —end note] [Example: given
#include <string> struct C { std::string s; // std::string as in (Clause 21) }; int main() { C a; C b = a; C c = std::string("hello"); b = a; b = std::string("world"); }
the implementation may implicitly define functions to make the definition of C equivalent to
struct C { std::string s; C(): s() { } C(const C& x): s(x.s) { } C(C&& x): s(static_cast<std::string&&>(x.s)) { } // : s(std::move(x.s)) { } C& operator=(const C& x) { s = x.s; return *this; } C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; } // { s = std::move(x.s); return *this; } ~C() { } };
—end example]
—end note]
Edit paragraph 2 as follows.
An expression is potentially evaluated unless it is an unevaluated operand (Clause 5) or a subexpression thereof. An object or non-overloaded function whose name appears as a potentially-evaluated expression is used unless it is an object that satisfies the requirements for appearing in a constant expression (5.19) and the lvalue-to-rvalue conversion (4.1) is immediately applied. A virtual member function is used if it is not pure. An overloaded function is used if it is selected by overload resolution when referred to from a potentially-evaluated expression. [Note: this covers calls to named functions (5.2.2), operator overloading (Clause 13), user-defined conversions (12.3.2), allocation function for placement new (5.3.4), as well as non-default initialization (8.5). A copy constructor or move constructor is used even if the call is actually elided by the implementation. —end note] An allocation or deallocation function for a class is used by a new expression appearing in a potentially-evaluated expression as specified in 5.3.4 and 12.5. A deallocation function for a class is used by a delete expression appearing in a potentially-evaluated expression as specified in 5.3.5 and 12.5. A non-placement allocation or deallocation function for a class is used by the definition of a constructor of that class. A non-placement deallocation function for a class is used by the definition of the destructor of that class, or by being selected by the lookup at the point of definition of a virtual destructor (12.4). [Footnote: An implementation is not required to call allocation and deallocation functions from constructors or destructors; however, this is a permissible implementation technique. —end footnote] A copy-assignment function for a class is used by an implicitly-defined copy-assignment function for another class as specified in 12.8. A move-assignment function for a class is used by an implicitly-defined move-assignment function for another class as specified in [class.copy]. A default constructor for a class is used by default initialization or value initialization as specified in 8.5. A constructor for a class is used as specified in 8.5. A destructor for a class is used as specified in 12.4.
Edit paragraph 2 as follows.
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, except that a class object or its copy/move may be eliminated as specified in 12.8.
Edit paragraph 3 as follows.
If a named automatic object 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 it appears to be unused, except that a class object or its copy/move may be eliminated as specified in 12.8.
Edit within paragraph 2 as follows.
....
the value of an object whose value was copied or moved from a traceable pointer object, where at the time of the copy or move the source object contained a copy of a safely-derived pointer value.
....
Edit within paragraph 3 as follows.
....
the value of an object whose value was copied or moved from a traceable pointer object, where at the time of the copy or move, the source object contained an integer representation of a safely-derived pointer value;
....
Edit within paragraph 1 as follows.
The lifetime of an object is a runtime property of the object. An object is said to have non-trivial initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. [Note: initialization by a trivial copy/move constructor is non-trivial initialization. —end note] The lifetime of an object of type T begins when: ....
Edit within paragraph 2 as follows.
For any object (other than a base-class subobject) of trivially copyable or trivially movable type
T
, 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. [Footnote: By using, for example, the library functions (17.6.1.2)std::memcpy
orstd::memmove
. —end footnote] 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 trivially copyable or trivially movable type
T
, 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 that t2p points to an initialized object ... std::memcpy(t1p, t2p, sizeof(T)); // at this point, // every subobject of trivially copyable // or trivially movable type in *t1p //
in *t1pcontains 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 of Nunsigned char
objects taken up by the object of typeT
, where N equalssizeof(T)
. The value representation of an object is the set of bits that hold the value of typeT
. For trivially copyable or trivially movable 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 10 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 andcv-qualifiedcv-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, trivially movable class types (Clause 9), arrays of such types, and cv-qualified versions of these types (3.9.3) are collectively called trivially movable 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 12 as follows. Note that literal types continue to not require a trivial move constructor.
A type is a literal type if it is:
a scalar type; or
a class type (Clause 9) with
a trivial copy constructor,
no non-trivial move constructor,
a trivial destructor,
a trivial default constructor or at least one constexpr constructor other than the copy or move constructor, and
all non-static data members and base classes of literal types; or
an array of literal type.
Note that this wording comes from N2927, which has been adopted but which has not yet appeared in a working draft.
Edit within paragraph 3 as follows.
.... An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:
the size and/or alignment of the closure type
whether the closure type is trivially copyable or trivially movable (_class_ Clause 9)
whether the closure type is a standard-layout class (_class_ Clause 9)
whether the closure type is a POD class (_class_ Clause 9)
An implementation shall not add members of rvalue reference type to the closure type.
Edit paragraph 18 as follows.
The closure type associated with a lambda-expression has a deleted ([dcl.fct.def.delete]) default constructor, a deleted move assignment operator, and a deleted copy assignment operator. It has an implicitly-declared copy constructor and may have an implicitly-declared move constructor (_class.copy_ 12.8). [Note: The copy/move constructor is implicitly defined in the same way as any other implicitly declared copy/move constructor would be implicitly defined. —end note]
Delete paragraph 19.
The closure type C associated with a lambda-expression has an additional public inline constructor with a single parameter of type C&&
. Given an argument objectx
, this constructor initializes each non-static data memberm
of*this
as follows:
ifm
is an array, each element ofthis->m
is direct-initialized with an expression equivalent to std::move(e) where e is the corresponding element ofx.m
. Otherwise,
ifm
is an lvalue reference,this->m
is initialized withx.m
. [Note:m
cannot be an rvalue reference. —end note] Otherwise,
this->m
is direct-initialized with an expression equivalent tostd::move(x.m)
.
[Note: The notations are for exposition only; the members of a closure type are unnamed and std::move need not be called. —end note]
Note that paragraph 21 is unchanged because named variables are not moved.
When the lambda-expression is evaluated, the entities that are captured by copy are used to direct-initialize each corresponding non-static data member of the resulting closure object. (For array members, the array elements are direct-initialized in increasing subscript order.) These initializations are performed in the (unspecified) order in which the non-static data members are declared. [Note: This ensures that the destructions will occur in the reverse order of the constructions. —end note]
Edit paragraph 4 as follows.
If the left operand is of class type, the class shall be complete. Assignment to objects of a class is defined by the copy/move assignment operator (12.8, 13.5.3).
Edit paragraph 2 as follows.
A return statement without an expression can be used only in functions that do not return a value, that is, a function with the return type
void
, a constructor (12.1), or a destructor (12.4). A return statement with an expression of non-void type can be used only in functions returning a value; the value of the expression is returned to the caller of the function. The expression is implicitly converted to the return type of the function in which it appears. A return statement can involve the construction and copy or move of a temporary object (12.2). [Note: A copy or move operation associated with a return statement may be elided or considered as an rvalue for the purpose of overload resolution in selecting a constructor (12.8). —end note] A return statement with a braced-init-list initializes the object or reference to be returned from the function by copy-list-initialization (8.5.4) from the specified initializer list. [Example:std::pair<std::string,int> f(const char* p, int x) { return {p,x}; }
—end example] Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.
Edit within paragraph 4 as follows.
.... A trivial copy/move constructor is also a constexpr constructor. ....
Edit paragraph 4 follows.
[Note: Since destructors do not have names, a using-declaration cannot refer to a destructor for a base class. Since specializations of member templates for conversion functions are not found by name lookup, they are not considered when a using-declaration specifies a conversion function (14.6.2). —end note] If an assignment operator brought from a base class into a derived class scope has the signature of a
copy-assignmentcopy/move assignment operator for the derived class (12.8), the using-declaration does not by itself suppress the implicit declaration of the derived classcopy-assignment operator; thecopy-assignmentcopy/move assignment operator from the base class is hidden or overridden by the implicitly-declaredcopy-assignmentcopy/move assignment operator of the derived class, as described below.
End the section after paragraph 8. The intent is to make defaulted and deleted definitions have their own section for citations.
Add a new section comprising paragraph 9 of the old 8.4 Function definitions [dcl.fct.def].
Split the paragraph into three paragraphs, with the first paragraph containing the introductory sentence, the second paragraph containing the special member discussion, and the third paragraph containing the note and example. The intent is to structure the discussion better for citations.
Edit the resulting second paragraph as follows.
Only specialSpecial member functions may be explicitly defaulted, and the implementation shall define them as if they had implicit definitions (12.1, 12.4, 12.8). A special member function that would be implicitly defined as deleted shall not be explicitly defaulted. A special member function is user-provided if it is user-declared and not explicitly defaulted on its first declaration. A user-provided explicitly-defaulted function is defined at the point where it is explicitly defaulted.
Edit the resulting third paragraph as follows.
[Example:
struct trivial { trivial() = default; trivial(const trivial&) = default; trivial(trivial&&) = default; trivial& operator =(const trivial&) = default; trivial& operator =(trivial&&) = default; ~trivial() = default; }; struct nontrivial1 { nontrivial1(); }; nontrivial1::nontrivial1() = default; // not inline struct nontrivial2 { nontrivial2(); }; inline nontrivial2::nontrivial2() = default; // not first declaration struct nontrivial3 { virtual ~nontrivial3() = 0; // virtual }; inline nontrivial3::~nontrivial3() = default; // not first declaration
—end example]
Add a new section comprising paragraph 10 of the old 8.4 Function definitions [dcl.fct.def]. Split that paragraph as follows. The intent is to structure the section better for citations.
The first paragraph comprises the introductory text. It will be as follows.
A function definition of the form:
decl-specifier-seqopt attribute-specifieropt declarator
= delete ;
is called a deleted definition. A function with a deleted definition is also called a deleted function.
The second paragraph comprises the explanatory text. It will be as follows.
A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed. [Note: this includes calling the function implicitly or explicitly and forming a pointer or pointer-to-member to the function. It applies even for references in expressions that are not potentially-evaluated. If a function is overloaded, it is referenced only if the function is selected by overload resolution. —end note]
The third paragraph comprises the primary examples. Edit it as follows.
[Example: One can enforce non-default initialization and non-integral initialization with
struct sometype { sometype() = delete; // redundant, but legal sometype(std::intmax_t) = delete; sometype(double); };
—end example] [Example: One can prevent use of a class in certain new expressions by using deleted definitions of a user-declared operator new for that class.
struct sometype { void *operator new(std::size_t) = delete; void *operator new[](std::size_t) = delete; }; sometype *p = new sometype; // error, deleted class operator new sometype
*p*q = new sometype[3]; // error, deleted class operator new[]—end example] [Example: One can make a class uncopyable, i.e. move-only, by using deleted definitions of the copy constructor and copy-assignment operator, and then providing defaulted definitions of the move constructor and the move assignment operator.
struct moveonly { moveonly() = default; moveonly(const moveonly&) = delete; moveonly(moveonly&&) = default; moveonly& operator=(const moveonly&) = delete; moveonly& operator=(moveonly&&) = default; ~moveonly() = default; }; moveonly *p; moveonly q(*p); // error, deleted copy constructor
—end example]
The fourth paragraph comprises restrictive clauses.
A deleted function is implicitly inline. [Note: the one-definition rule (3.2) applies to deleted definitions. —end note] A deleted definition of a function shall be the first declaration of the function. [Example:
struct sometype { sometype(); }; sometype::sometype() = delete; // ill-formed; not first declaration
—end example]
Edit paragraph 14 as follows.
The initialization that occurs in the form
T x = a;
as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called
copy-initializationcopy initialization. [Note: The copy initialization may invoke a move ([class.copy]). —end note]
Edit paragraph 3 as follows.
Complete objects and member subobjects of class type shall have nonzero size. [Footnote: Base class subobjects are not so constrained. —end footnote] [Note: class objects can be assigned, passed as arguments to functions, and returned by functions (except objects of classes for which copying or moving has been restricted; see 12.8). Other plausible operators, such as equality comparison, can be defined by the user; see 13.5. —end note]
Edit paragraph 6 as follows.
A trivially copyable class is a class that:
has no non-trivial copy constructors (12.8),
has no non-trivial move constructors ([class.copy]),
has no non-trivial copy assignment operators (13.5.3, 12.8),
andhas no non-trivial move assignment operators (13.5.3, [class.copy]), and
has a trivial destructor (12.4).
A trivially movable class is a class that:
has trivial move constructors ([class.copy]),
has trivial move assignment operators (13.5.3, [class.copy]), and
has a trivial destructor (12.4).
[Note: A class may not have a move constructor or a move assignment operator ([class.copy]), in which case it is neither trivially movable nor non-trivially movable. —end note] A trivial class is a class that has a trivial default constructor (12.1) and is trivially copyable. [Note: in particular, a trivially copyable, trivially movable, or trivial class does not have virtual functions or virtual base classes. —end note]
Split and edit paragraph 1 as follows. The first part of paragraph 1 remains paragraph 1. The intent is to structure the discussion better for citations.
In a union, at most one of the data members can be active at any time, that is, the value of at most one of the data members can be stored in a union at any time. [Note: one special guarantee is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout structs that share a common initial sequence (9.2), and if an object of this standard-layout union type contains one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of standard-layout struct members; see 9.2. —end note] The size of a union is sufficient to contain the largest of its data members. Each data member is allocated as if it were the sole member of a struct.
The second part of paragraph 1 becomes a new paragraph. Edit that paragraph as follows. Note that non-movable classes may still be copyable. Note the lone added comma after "reference type".
A union can have member functions (including constructors and destructors), but not virtual (10.3) functions. A union shall not have base classes. A union shall not be used as a base class. If a union contains a non-static data member of reference type, the program is ill-formed. [Note: if any non-static data member of a union has a non-trivial default constructor (12.1), copy constructor (12.8), move constructor (12.8), copy assignment operator (12.8), move assignment operator (12.8), or destructor (12.4), the corresponding member function of the union must be user-provided or it will be implicitly deleted ([dcl.fct.def.delete]) for the union. —end note]
The third part of paragraph 1, consisting of the string example, becomes a new paragraph. Edit that paragraph as follows.
[Example: Consider the following union:
union U { int i; float f; std::string s; };
Since
std::string
(21.3) declares non-trivial versions of all of the special member functions,U
will have an implicitly deleted default constructor, copy/move constructor, copy/move assignment operator, and destructor. To useU
, some or all of these member functions must beuser-declareduser-provided. —end example]
The fourth part of paragraph 1, consisting of the example changing the active member, becomes a new paragraph. Edit that paragraph as follows.
[Note: In general, one must use explicit destructor calls and placement new operators to change the active member of a union. —end note] [Example: Consider an object
u
of a union typeU
having non-static data membersm
of typeM
andn
of typeN
. IfM
has a non-trivial destructor andN
has a non-trivial constructor (for instance, if they declare or inherit virtual functions), the active member ofu
can be safely switched fromm
ton
using the destructor and placement new operator as follows:u.m.~M(); new (&u.n) N;
—end example]
Split and edit paragraph 1 as follows. This first part of paragraph 1 remains paragraph 1. The second part, about refering implicitly-defined member functions, becomes a new paragraph. The intent is to structure the discussion better for citations.
The default constructor (12.1), copy constructor and copy assignment operator (12.8), move constructor and move assignment operator ([class.copy]), and destructor (12.4) are special member functions. [Note: The implementation will implicitly declare these member functions for some class types when the program does not explicitly declare them. The implementation will implicitly define them if they are used. See 12.1, 12.4 and 12.8. —end note] Programs shall not define implicitly-declared special member functions.
Edit paragraph 2 as follows.
[Note: the special member functions affect the way objects of class type are created, copied, moved, and destroyed, and how values can be converted to values of other types. Often such special member functions are called implicitly. —end note]
Edit paragraph 9 as follows.
A copy constructor (12.8) is used to copy objects of class type. A move constructor ([class.copy]) is used to move the contents of objects of class type.
Merge and split existing paragraphs 1 and 2. Edit them as follows. The first part of paragraph 1 remains paragraph.
Temporaries of class type are created in various contexts: binding an rvalue to a reference (8.5.3), returning an rvalue (6.6.3), a conversion that creates an rvalue (4.1, 5.2.9, 5.2.11, 5.4), throwing an exception (15.1), entering a handler (15.3), and in some initializations (8.5). [Note: the lifetime of exception objects is described in 15.1. —end note] Even when the creation of the temporary object is avoided (12.8), all the semantic restrictions shall be respected as if the temporary object had been created.
[Example:[Note: even if the copy/move constructor is not called, all the semantic restrictions, such as accessibility (Clause 11), shall be satisfied.—end example]—end note]
Take the last part, the true example, of the existing paragraph 1 and merge it into the existing paragraph 2.
[Example: Consider the following code.
class X { public: X(int); X(const X&); ~X(); }; class Y { public: Y(int); Y(Y&&); ~Y(); }; X f(X); Y g(Y); void h() { X a(1); X b = f(X(2)); Y c = g(Y(3)); a = f(a); }
Here, anAn implementation might use a temporary in which to constructX(2)
before passing it tof()
usingX
'scopy-constructorcopy constructor; alternatively,X(2)
might be constructed in the space used to hold the argument. Likewise, an implementation might use a temporary in which to constructY(3)
before passing it tog()
usingY
's move constructor; alternatively,Y(3)
might be constructed in the space used to hold the argument. Also, a temporary might be used to hold the result off(X(2))
before copying it tob
usingX
'scopy-constructorcopy constructor; alternatively,f()
's result might be constructed inb
. Likewise, a temporary might be used to hold the result ofg(Y(3))
before moving it toc
usingY
's move constructor; alternatively,g()
's result might be constructed inc
. On the other hand, the expressiona=f(a)
requires a temporary for the result off(a)
, which is then assigned toa
. —end example]
Edit paragraph 3 as follows.
A non-explicit
copy-constructorcopy/move constructor (12.8) is a converting constructor. An implicitly-declared copy/move constructor is not an explicit constructor; it may be called for implicit type conversions.
Within the example of paragraph 1, change uses of "copy" to "copy/move".
Edit the section title as above.
Edit paragraph 1 as follows.
A class object can be copied or moved in two ways, by initialization (12.1, 8.5), including for function argument passing (5.2.2) and for function value return (6.6.3), and by assignment (5.17). Conceptually, these two operations are implemented by a copy/move constructor (12.1) and copy/move assignment operator (13.5.3).
Split the existing paragraph 2 into two parts. The intent is to structure the discussion better for citations and to make room for a move specification. Edit the first part as follows. Note that the footnote is not deleted, but moved to later in the text.
A non-template constructor for class
X
is a copy constructor if its first parameter is of typeX&
,const X&
,volatile X&
orconst volatile X&
, and either there are no other parameters or else all other parameters have default arguments (8.3.6).[Footnote: Because a template constructor or a constructor whose first parameter is an rvalue reference is never a copy constructor, the presence of such a constructor does not suppress the implicit declaration of a copy constructor. Such constructors participate in overload resolution with other constructors, including copy constructors, and, if selected, will be used to copy an object. —end footnote][Example:X::X(const X&)
andX::X(X&,int=1)
are copy constructors.struct X { X(int); X(const X&, int = 1); }; X a(1); // calls X(int); X b(a, 0); // calls X(const X&, int); X c = b; // calls X(const X&, int);
—end example]
After the first part of the existing paragraph 2, add a new paragraph as follows.
A non-template constructor for class
X
is a move constructor if its first parameter is of typeX&&
,const X&&
,volatile X&&
orconst volatile X&&
, and either there are no other parameters or else all other parameters have default arguments (8.3.6). [Example:Y::Y(X&&)
is a move constructor.struct Y { Y(const Y&); Y(Y&&); }; extern Y f(int); Y d(f(1)); // calls Y(Y&&); Y e = d; // calls Y(const Y&);
—end example]
Edit the second part of the existing paragraph 2 as follows.
[Note: all forms of copy/move constructor may be declared for a class. [Example:
struct X { X(const X&); X(X&); // OK X(X&&); X(const X&&); // OK, but possibly not sensible };
—end example] —end note] [Note: if a class
X
only has a copy constructor with a parameter of typeX&
, an initializer of typeconst X
orvolatile X
cannot initialize an object of type (possibly cv-qualified)X
. [Example:struct X { X(); // default constructor X(X&); // copy constructor with a nonconst parameter }; const X cx; X x = cx; // error: X::X(X&) cannot copy cx into x
—end example] —end note]
Split and edit paragraph 3 as follows. The first sentence remains within the paragraph.
A declaration of a constructor for a class
X
is ill-formed if its first parameter is of type (optionally cv-qualified)X
and either there are no other parameters or else all other parameters have default arguments.
Add a new paragraph consisting of the remainder of the existing paragraph 3 and edited as follows.
A member function template is never instantiated to perform the copy of a class object to an object of its class type. [Example:
struct S { template<typename T> S(T&); template<typename T> S(T&&); S(); }; S f(); const S g; void h() { S a( f() ); // does not instantiate member template // uses the implicitly generated move constructor S a( g ); // does not instantiate member template // uses the implicitly generated copy constructor }
—end example]
Edit the existing paragraph 4 as follows.
If the class definition does not explicitly declare a copy constructor, and there is no user-declared move constructor, one is declared implicitly. Thus, for the class definition
struct X { X(const X&, int); };
a copy constructor is implicitly-declared. If the user-declared constructor is later defined as
X::X(const X& x, int i =0) { /* ... */ }
then any use of
X
's copy constructor is ill-formed because of the ambiguity; no diagnostic is required.
Split the existing paragraph 5 into two parts, the form of copy constructors and the deleting of copy constructors. The first part remains unchanged as follows.
The implicitly-declared copy constructor for a class
X
will have the formX::X(const X&)
if
each direct or virtual base class
B
ofX
has a copy constructor whose first parameter is of typeconst B&
orconst volatile B&
, andfor all the non-static data members of
X
that are of a class typeM
(or array thereof), each such class type has a copy constructor whose first parameter is of typeconst M&
orconst volatile M&
. [Footnote: This implies that the reference parameter of the implicitly-declared copy constructor cannot bind to a volatile lvalue; see C.1.8. —end footnote]Otherwise, the implicitly-declared copy constructor will have the form
X::X(X&)
After the first part of the existing paragraph 5, insert a new paragraph as follows.
The default definition of the move constructor for a class
X
is well-formed if and only if
each direct or virtual base class
B
ofX
has a move constructor, andeach non-static member of
X
has a move constructor or is trivially movable.If the class definition does not explicitly declare a move constructor, one will be declared implicitly if and only if
The default definition of the move constructor for
X
is well-formed, and
X
does not have a user-declared copy constructor.[Note: When the move constructor is not implicitly declared or explicitly supplied, expressions that otherwise would have invoked the move constructor may instead invoke a copy constructor. —end note]
After the new paragraph above, insert a new paragraph as follows.
The implicitly-declared move constructor for class
X
will have the formX::X(X&&)
Edit the second part of the existing paragraph 5 as follows.
An implicitly-declared copy/move constructor is an
inline public
member of its class. An implicitly-declared copy/move constructor for a classX
is defined as deleted ([dcl.fct.def.delete]) ifX
has:
a variant member with a non-trivial
copycorresponding constructor andX
is a union-like classa non-static data member of class type
M
(or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied toM
'scopycorresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the implicitly-declaredcopyconstructor, ora direct or virtual base class
B
that cannot be copied/moved because overload resolution (13.3), as applied toB
'scopycorresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the implicitly-declaredcopyconstructor.
Edit the existing paragraph 6 as follows.
A copy/move 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), andthe constructor selected to copy/move each direct base class subobject is trivial, and
for each non-static data
memberofmember ofX
that is of class type (or array thereof), the constructor selected to copy/move that member is trivial;otherwise the copy/move constructor is non-trivial.
Edit paragraph 7 as follows.
A non-user-provided copy/move constructor is implicitly defined if it is used to initialize an object of its class type from a copy of an object of its class type or of a class type derived from its class type. [Footnote: See 8.5 for more details on direct and copy initialization. —end footnote] [Note: the copy/move constructor is implicitly defined even if the implementation elided its use (12.2). —end note] A program is ill-formed if the implicitly-defined copy/move constructor is explicitly defaulted, but the corresponding implicit declaration would have been deleted.
Before the non-user-provided copy/move constructor for a class is implicitly defined, all non-user-provided copy/move constructors for its direct and virtual base classes and its non-static data members shall have been implicitly defined. [Note: an implicitly-declared copy/move constructor has an exception-specification (15.4). An explicitly-defaulted definition ([dcl.fct.def.default]) has no implicit exception-specification. —end note]
Paragraph 8 is unchanged as follows.
The implicitly-defined or explicitly-defaulted copy constructor for class
X
performs a memberwise copy of its subobjects. [Note: brace-or-equal-initializers of non-static data members are ignored. See also the example in 12.6.2. —end note] The order of copying is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2). Each subobject is copied in the manner appropriate to its type:
if the subobject is of class type, the copy constructor for the class is used;
if the subobject is an array, each element is copied, in the manner appropriate to the element type;
if the subobject is of scalar type, the built-in assignment operator is used.
Virtual base class subobjects shall be copied only once by the implicitly-defined copy constructor (see 12.6.2).
Add a new paragraph after paragraph 8 above.
The implicitly-defined or explicitly-defaulted move constructor for a non-union class
X
performs a memberwise move of its subobjects. [Note: brace-or-equal-initializers of non-static data members are ignored. See also the example in 12.6.2. —end note] The order of moving is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2). Given an argument objectx
, each memberm
of reference typeR
is moved by initializingthis->m
withstatic_cast<R&&>(x.m)
. Each subobject is moved in the manner appropriate to its type:
if the subobject is a named member
c
of class typeC
,c
is direct-initialized with the expressionstatic_cast<C&&>(x.c)
, the move constructor for the class is used;if the subobject is a base class
B
, the base is direct-initialized with the expressionstatic_cast<B&&>(x)
, the move constructor for the class is used;if the subobject is an array, each element is moved, in the manner appropriate to the element type;
if the subobject is of scalar type, the built-in assignment operator is used.
Virtual base class subobjects shall be moved only once by the implicitly-defined move constructor (see 12.6.2).
Split the existing paragraph 10 into four paragraphs,
the form part, the general part, the deleted
part,
and the hiding/using part.
Edit the first form part as follows.
If the class definition does not explicitly declare a copy assignment operator, and there is no user-declared move assignment operator, one is declared implicitly. The implicitly-declared copy assignment operator for a class
X
will have the formX& X::operator=(const X&)
if
- each direct base class
B
ofX
has a copy assignment operator whose parameter is of typeconst B&
,const volatile B&
orB
, and- for all the non-static data members of
X
that are of a class typeM
(or array thereof), each such class type has a copy assignment operator whose parameter is of typeconst M&
,const volatile M&
orM
. [Footnote: This implies that the reference parameter of the implicitly-declared copy assignment operator cannot bind to a volatile lvalue; see C.1.8. —end note]Otherwise, the implicitly-declared copy assignment operator will have the form
X& X::operator=(X&)
After the first form part, add a new paragraph as follows.
A user-declared move assignment operator
X::operator=
is a non-static non-template member function of classX
with exactly one parameter of typeX&&
,const X&&
,volatile X&&
orconst volatile X&&
. [Note: an overloaded assignment operator must be declared to have only one parameter; see 13.5.3. —end note] [Note: more than one form of move assignment operator may be declared for a class. —end note]
After the above new paragraph, add a new paragraph as follows.
The default definition of the move assignment operator for a class
X
is well-formed if an only if:
each direct base class
B
ofX
has a move assignment operator,there are no direct or indirect virtual bases of
X
, andfor all the non-static data members of
X
that are of a class typeM
(or array thereof), each such class type has a move assignment operator.If the class definition does not explicitly declare a move assignment operator, one will be declared implicitly if and only if
the default definition of the move assignment operator is well-formed, and
the copy assignment operator is not user-declared.
[Example: The class definition
struct S { int a; S& operator=( const S& ) = default; };
will not have a default move assignment operator implicitly declared because the copy assignment operator has been user-declared. The move assignment operator may be explicitly defaulted.
struct S { int a; S& operator=( const S& ) = default; S& operator=( S&& ) = default; };
—end cxample]
After the above new paragraph, add a new paragraph as follows.
The implicitly-declared move assignment operator for a class
X
will have the formX& X::operator=(X&&)
Edit the second part of the existing paragraph 10 into a new paragraph as follows.
The implicitly-declared copy/move assignment operator for class
X
has the return typeX&
; it returns the object for which the assignment operator is invoked, that is, the object assigned to. An implicitly-declared copy/move assignment operator is an inline public member of its class.
Edit the third part of the existing paragraph 10 into a new paragraph as follows.
An implicitly-declared copy/move assignment operator for class
X
is defined as deleted ifX
has:
a variant member with a non-trivial
copycorresponding assignment operator andX
is a union-like class,a non-static data member of const non-class type (or array thereof), or
a non-static data member of reference type, or
a non-static data member of class type
M
(or array thereof) that cannot be copied/moved because overload resolution (13.3), as applied toM
'scopycorresponding assignment operator, results in an ambiguity or a function that is deleted or inaccessible from the implicitly-declaredcopyassignment operator, ora direct or virtual base class
B
that cannot be copied/moved because overload resolution (13.3), as applied toB
'scopycorresponding assignment operator, results in an ambiguity or a function that is deleted or inaccessible from the implicitly-declaredcopyassignment operator.
Edit the fourth part of the existing paragraph 10 into a new paragraph as follows.
Because a copy/move assignment operator is implicitly declared for a class if not declared by the user, a base class copy/move assignment operator is always hidden by the
copycorresponding assignment operator of a derived class (13.5.3). A using-declaration (7.3.3) that brings in from a base class an assignment operator with a parameter type that could be that of acopy-assignmentcopy/move assignment operator for the derived class is not considered an explicit declaration of sucha copy-assignmentan operator and does not suppress the implicit declaration of the derived classcopy-assignmentoperator; the operator introduced by the using-declaration is hidden by the implicitly-declaredcopy-assignmentoperator in the derived class.
Edit paragraph 11 as follows.
A copy/move 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), andthe assignment operator selected to copy/move each direct base class subobject is trivial, and
for each non-static data
memberofmember ofX
that is of class type (or array thereof), the assignment operator selected to copy/move that member is trivial;otherwise the copy/move assignment operator is non-trivial.
Edit paragraph 12 as follows.
A non-user-provided copy/move assignment operator is implicitly defined when an object of its class type is assigned a value of its class type or a value of a class type derived from its class type. A program is ill-formed if the implicitly-defined copy/move assignment operator is explicitly defaulted, but the corresponding implicit declaration would have been deleted. Before the non-user-provided copy/move assignment operator for a class is implicitly defined, all non-user-provided copy assignment operators for its direct base classes and its non-static data members shall have been implicitly defined. [Note: an implicitly-declared copy/move assignment operator has an exception-specification (15.4). An explicitly-defaulted definition has no implicit exception-specification. —end note]
Edit paragraph 13 as follows.
The implicitly-defined or explicitly-defaulted copy assignment operator for class
X
performs memberwise copy assignment of its subobjects. The direct base classes ofX
are assigned first, in the order of their declaration in the base-specifier-list, and then the immediate non-static data members ofX
are assigned, in the order in which they were declared in the class definition. Each subobject is assigned in the manner appropriate to its type:
if the subobject is of class type, the copy assignment operator for the class is used (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes);
if the subobject is an array, each element is assigned, in the manner appropriate to the element type;
if the subobject is of scalar type, the built-in assignment operator is used.
It is unspecified whether subobjects representing virtual base classes are assigned more than once by the implicitly-defined or explicitly-defaulted copy assignment operator. [Example:
struct V { }; struct A : virtual V { }; struct B : virtual V { }; struct C : B, A { };
It is unspecified whether the virtual base class subobject
V
is assigned twice by the implicitly-defined copy assignment operator for C. —end example]
Add a new paragraph after paragraph 13 as follows.
The implicitly-defined or explicitly-defaulted move assignment operator for a non-union class
X
performs memberwise assignment of its subobjects. The direct base classes ofX
are assigned first, in the order of their declaration in the base-specifier-list, and then the immediate non-static data members ofX
are assigned, in the order in which they were declared in the class definition. Each subobject is assigned in the manner appropriate to its type:
if the subobject is a named member
c
of class typeC
,c
is move-assigned with the expressionstatic_cast<C&&>(x.c)
, the move assignment operator for the class is used (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes);if the subobject is a base class
B
, the base is move-assigned with the expressionstatic_cast<B&&>(x)
, the move assignment operator for the class is used (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes);if the subobject is an array, each element is moved, in the manner appropriate to the element type;
if the subobject is of scalar type, the built-in assignment operator is used.
A non-trivial move assignment operator shall be invoked exactly once for each virtual base subobject.
Edit paragraph 14 as follows.
A program is ill-formed if the copy/move constructor or the copy/move assignment operator for an object is implicitly used and the special member function is not accessible (Clause 11). [Note: Copying/moving one object into another using the copy/move constructor or the copy/move assignment operator does not change the layout or size of either object. —end note]
Edit paragraph 15 as follows.
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. [Footnote: Because only one object is destroyed instead of two, and one copy/move constructor is not executed, there is still one object destroyed for each one constructed. —end footnote] This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value
in a throw-expression, when the operand is the name of a non-volatile automatic object, the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object
when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
when the exception-declaration of an exception handler (Clause 15) declares an object of the same type (except for cv-qualification) as the exception object (15.1), the copy/move operation can be omitted by treating the exception-declaration as an alias for the exception object if the meaning of the program will be unchanged except for the execution of constructors and destructors for the object declared by the exception-declaration.
[Example:
class Thing { public: Thing(); ~Thing(); Thing(const Thing&); }; Thing f() { Thing t; return t; } Thing t2 = f();
Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class
Thing
: the copying of the local automatic objectt
into the temporary object for the return value of functionf()
and the copying of that temporary object into objectt2
. Effectively, the construction of the local objectt
can be viewed as directly initializing the global objectt2
, and that object's destruction will occur at program exit. Adding a move constructor toThing
has the same effect, but it is the move construction from the temporary object tot2
that is elided. —end example]
Paragraph 16 is unchanged as follows.
When the criteria for elision of a copy operation are met and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note] [Example:
class Thing { public: Thing(); ~Thing(); Thing(Thing&&); private: Thing(const Thing&); }; Thing f(bool b) { Thing t; if (b) throw t; // OK: Thing(Thing&&) used (or elided) to throw t return t; // OK: Thing(Thing&&) used (or elided) to return t } Thing t2 = f(false); // OK: Thing(Thing&&) used (or elided) to construct
oft2—end example]
Edit paragraph 3 as follows.
For each non-template constructor in the candidate set of inherited constructors other than a constructor having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly declared with the same constructor characteristics unless there is a user-declared constructor with the same signature in the class where the using-declaration appears. Similarly, for each constructor template in the candidate set of inherited constructors, a constructor template is implicitly declared with the same constructor characteristics unless there is an equivalent user-declared constructor template (14.6.6.1) in the class where the using-declaration appears. [Note: Default arguments are not inherited. —end note]
Edit paragraph 5 as follows.
[Note: Default and copy/move constructors may be implicitly declared as specified in 12.1 and 12.8. —end note]
Edit paragraph 6 as follows.
[Example:
struct B1 { B1(int); }; struct B2 { B2(int = 13, int = 42); }; struct D1 : B1 { using B1::B1; }; struct D2 : B2 { using B2::B2; };
The candidate set of inherited constructors in
D1
forB1
is
B1(const B1&)
B1(B1&&)
B1(int)
The set of constructors present in
D1
is
D1()
, implicitly-declared default constructor, ill-formed if used
D1(const D1&)
, implicitly-declared copy constructor, not inherited
D1(D1&&)
, implicitly-declared move constructor, not inherited
D1(int)
, implicitly-declared inheriting constructorThe candidate set of inherited constructors in
D2
forB2
is
B2(const B2&)
B2(B2&&)
B2(int = 13, int = 42)
B2(int = 13)
The set of constructors present in
D2
is
D2()
, implicitly-declared default constructor, not inherited
D2(const D2&)
, implicitly-declared copy constructor, not inherited
D2(D2&&)
, implicitly-declared move constructor, not inherited
D2(int, int)
, implicitly-declared inheriting constructor
D2(int)
, implicitly-declared inheriting constructor—end example]
Edit paragraph 4 as follows.
However, when considering the argument of a user-defined conversion function that is a candidate by 13.3.1.3 when invoked for the copying/moving of the temporary in the second step of a class copy-initialization, by 13.3.1.7 when passing the initializer list as a single argument or when the initializer list has exactly one element and a conversion to some class
X
or reference to (possibly cv-qualified)X
is considered for the first parameter of a constructor ofX
, or by 13.3.1.4, 13.3.1.5, or 13.3.1.6 in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.
Edit paragraph 4 as follows.
A conversion of an expression of class type to the same class type is given Exact Match rank, and a conversion of an expression of class type to a base class of that type is given Conversion rank, in spite of the fact that a copy/move constructor (i.e., a user-defined conversion function) is called for those cases.
Paragraph 1 is unchanged. The move assignment operator does not change the hiding.
An assignment operator shall be implemented by a non-static member function with exactly one parameter. Because a copy assignment operator
operator=
is implicitly declared for a class if not declared by the user (12.8), a base class assignment operator is always hidden by the copy assignment operator of the derived class.
Edit paragraph 2 as follows.
Any assignment operator, even the copy/move assignment operator, can be virtual. [Note: for a derived class
D
with a base classB
for which a virtual copy/move assignment has been declared, the copy/move assignment operator inD
does not overrideB
's virtual copy/move assignment operator. [Example:struct B { virtual int operator= (int); virtual B& operator= (const B&); }; struct D : B { virtual int operator= (int); virtual D& operator= (const B&); }; D dobj1; D dobj2; B* bptr = &dobj1; void f() { bptr->operator=(99); // calls D::operator=(int) *bptr = 99; // ditto bptr->operator=(dobj2); // calls D::operator=(const B&) *bptr = dobj2; // ditto dobj1 = dobj2; // calls implicitly-declared // D::operator=(const D&) }
—end example] —end note]
Edit paragraph 5 as follows.
When the thrown object is a class object, the copy/move constructor and the destructor shall be accessible, even if the copy/move operation is elided (12.8).
Edit paragraph 13 as follows.
An implicitly declared special member function (Clause 12) shall have an exception-specification. If
f
is an implicitly declared default constructor, copy constructor, move constructor, destructor,orcopy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-idT
if and only ifT
is allowed by the exception-specification of a function directly invoked byf
's implicit definition;f
shall allow all exceptions if any function it directly invokes allows all exceptions, andf
shall allow no exceptions if every function it directly invokes allows no exceptions. [Example:struct A { A(); A(const A&) throw(); A(A&&) throw(); ~A() throw(X); }; struct B { B() throw(); B(const B&) throw(); B(B&&) throw(Y); ~B() throw(Y); }; struct D : public A, public B { // Implicit declaration of D::D(); // Implicit declaration of D::D(const D&) throw(); // Implicit declaration of D::D(D&&) throw(Y); // Implicit declaration of D::~D() throw(X,Y); };
Furthermore, if
A::~A()
orB::~B()
were virtual,D::~D()
would not be as restrictive as that ofA::~A
, and the program would be ill-formed since a function that overrides a virtual function from a base class shall have an exception-specification at least as restrictive as that in the base class. —end example]
Edit paragraph 2 as follows.
Descriptions of class member functions follow the order (as appropriate): [Footnote: To save space, items that do not apply to a class are omitted. For example, if a class does not specify any comparison functions, there will be no "Comparison functions" subclause. —end footnote]
constructor(s) and destructor
copying/moving & assignment functions
comparison functions
modifier functions
observer functions
operators and other non-member functions
Edit paragraph 1 as follows.
For the sake of exposition, Clauses 18 through 30 and Annex D do not describe copy/move constructors, assignment operators, or (non-virtual) destructors with the same apparent semantics as those that can be generated by default (12.1, 12.4, 12.8).
Edit the synopsis as follows.
namespace std { class type_index { public: type_index(const type_info& rhs); bool operator==(const type_index& rhs) const; bool operator!=(const type_index& rhs) const; bool operator< (const type_index& rhs) const; bool operator<= (const type_index& rhs) const; bool operator> (const type_index& rhs) const; bool operator>= (const type_index& rhs) const; size_t hash_code() const; const char* name() const; private: const type_info* target; // exposition only // Note that the use of a pointer here, rather than a reference, // means that the default copy/move constructor and assignment // operators will be provided and work as expected. }; }
Edit paragraph 2 as follows. Note that we do not require exceptions to be movable.
exception_ptr
shall be DefaultConstructible, CopyConstructible, CopyAssignable and EqualityComparable.exception_ptr
's operations shall not throw exceptions.
Paragraph 7 is unchanged. That is, move constructors cannot throw an exception as per the general rule.
Returns: An
exception_ptr
object that refers to the currently handled exception (15.3) or a copy of the currently handled exception, or a nullexception_ptr
object if no exception is being handled. The referenced object shall remain valid at least as long as there is anexception_ptr
object that refers to it. If the function needs to allocate memory and the attempt fails, it returns anexception_ptr
object that refers to an instance ofbad_alloc
. It is unspecified whether the return values of two successive calls tocurrent_exception
refer to the same exception object. [Note: that is, it is unspecified whethercurrent_exception
creates a new copy each time it is called. —end note] If the attempt to copy the current exception object throws an exception, the function returns anexception_ptr
object that refers to the thrown exception or, if this is not possible, to an instance ofbad_exception
. [Note: The copy constructor of the thrown exception may also fail, so the implementation is allowed to substitute abad_exception
object to avoid infinite recursion. —end note]
The changes in this section are relative to working draft N2723.
Edit table 33 as follows.
Table 33 — MoveConstructible
requirements [moveconstructible]Expression Post-condition T t = rv
The value of t
is equivalent to the value ofrv
before the construction.T t = std::move(u)
The value of t
is equivalent to that ofu
before construction. Eitheru
is unchanged or the construction shall not throw.[Note: There is no requirement on the value ofrv
after the construction. —end note]
Edit table 34 as follows.
Table 34 — CopyConstructible
requirements [copyconstructible]Expression Post-condition T t = u
theThe value ofu
is unchanged and equivalent tot
.T t = std::move(u)
The value of t
is equivalent to that ofu
before construction. Eitheru
is unchanged or the construction shall not throw.[Note: A type that satisfies the CopyConstructible
requirements also satisfies theMoveConstructible
requirements. —end note]
Edit table 35 as follows.
Table 35 — MoveAssignable
requirements [moveassignable]Expression Return type Return value Post-condition t = rv
T&
t
The value of t
is equivalent to the value ofrv
before the assignment.t = std::move(u)
T&
t
The value of t
is equivalent to that ofu
before construction. Eitheru
is unchanged or the construction shall not throw.[Note: There is no requirement on the value ofr_value after the assignment. —end note]rv
Edit table 36 as follows.
Table 36 — CopyAssignable
requirements [copyassignable]Expression Return type Return value Post-condition t = u
T&
t
The value of t
is equivalent tou
, the value ofu
is unchangedt = std::move(u)
T&
t
The value of t
is equivalent to that ofu
before assignment. Eitheru
is unchanged or the assignment shall not throw.[Note: A type that satisfies the CopyAssignable
requirements also satisfies theMoveAssignable
requirements. —end note]
Edit within the synopsis as follows.
// 20.6.4.3, type properties: template <class T> struct is_const; template <class T> struct is_volatile; template <class T> struct is_trivial; template <class T> struct is_standard_layout; template <class T> struct is_pod; template <class T> struct is_empty; template <class T> struct is_polymorphic; template <class T> struct is_abstract; template <class T> struct has_trivial_default_constructor; template <class T> struct has_trivial_copy_constructor; template <class T> struct has_trivial_move_constructor; template <class T> struct has_trivial_copy_assign; template <class T> struct has_trivial_move_assign; template <class T> struct has_trivial_destructor; template <class T> struct has_nothrow_default_constructor; template <class T> struct has_nothrow_copy_constructor; template <class T> struct has_nothrow_move_constructor; template <class T, class ...Args> struct is_nothrow_constructible; template <class T> struct has_nothrow_copy_assign; template <class T> struct has_nothrow_move_assign; template <class T> struct has_virtual_destructor; template <class T> struct is_signed; template <class T> struct is_unsigned; template <class T> struct alignment_of; template <class T> struct rank; template <class T, unsigned I = 0> struct extent;
Edit within Table 33 as follows.
Table 33 — Type property predicates Template Condition Preconditions .... .... .... template <class T> struct has_trivial_copy_constructor;
T
is a trivial type (3.9) or a reference type or a class type whose copy constructors (12.8) are all trivial.T
shall be a complete type, an array of unknown bound, or (possibly cv-qualified)void
.template <class T> struct has_trivial_move_constructor;
T
is a trivial type (3.9) or a reference type or a class type whose move constructors (12.8) are all trivial.T
shall be a complete type, an array of unknown bound, or (possibly cv-qualified)void
.template <class T> struct has_trivial_copy_assign;
T
is neitherconst
nor a reference type, andT
is a trivial type (3.9) or a class type whose copy assignment operators (12.8) are all trivial.T
shall be a complete type, an array of unknown bound, or (possibly cv-qualified)void
.template <class T> struct has_trivial_move_assign;
T
is neitherconst
nor a reference type, andT
is a trivial type (3.9) or a class type whose move assignment operators (12.8) are all trivial.T
shall be a complete type, an array of unknown bound, or (possibly cv-qualified)void
..... .... .... template <class T> struct has_nothrow_copy_constructor;
has_trivial_copy_constructor<T >::value
is true orT
is a class type whose copy constructors are all known not to throw any exceptions orT
is an array of such a class type.T
shall be a complete type, an array of unknown bound, or (possibly cv-qualified)void
.template <class T> struct has_nothrow_move_constructor;
has_trivial_move_constructor<T >::value
is true orT
is a class type whose move constructors are all known not to throw any exceptions orT
is an array of such a class type.T
shall be a complete type, an array of unknown bound, or (possibly cv-qualified)void
.template <class T> struct has_nothrow_copy_assign;
T
is neitherconst
nor a reference type, andhas_trivial_copy_assign<T >::value
is true orT
is a class type whose copy assignment operators taking an lvalue of typeT
are all known not to throw any exceptions orT
is an array of such a class type.T
shall be a complete type, an array of unknown bound, or (possibly cv-qualified)void
.template <class T> struct has_nothrow_move_assign;
T
is neitherconst
nor a reference type, andhas_trivial_move_assign<T >::value
is true orT
is a class type whose move assignment operators taking an rvalue of typeT
are all known not to throw any exceptions orT
is an array of such a class type.T
shall be a complete type, an array of unknown bound, or (possibly cv-qualified)void
.template <class T, class ...Args> struct is_nothrow_constructible;
Given the function prototype:
template <class T> typename add_rvalue_reference<T >::type create();
the predicate condition for a template specialization
is_nothrow_constructible<T, Args...>
shall be satisfied if and only if the following expression would be well-formed and is known not to throw an exception:
if
sizeof...(Args) == 1
the expressionstatic_cast<T>(create<Args>())
otherwise the expression
T(Args)
T
shall be a complete type..... .... ....
Edit paragraph 1 as follows.
reference_wrapper<T>
is a CopyConstructible and CopyAssignable wrapper around a reference to an object of typeT
.
Edit paragraph 1 as follows.
All placeholder types shall be
DefaultConstructible
andCopyConstructible
, and their default constructors and copy/move constructors shall not throw exceptions. It is implementation defined whether placeholder types areCopyAssignable
.CopyAssignable
placeholders' copy assignment operators shall not throw exceptions.
Edit paragraph 2 as follows.
Specializations of
shared_ptr
shall be CopyConstructible, CopyAssignable, and LessThanComparable, allowing their use in standard containers. Specializations ofshared_ptr
shall be convertible tobool
, allowing their use in boolean expressions and declarations in conditions. The template parameterT
ofshared_ptr
may be an incomplete type.
Edit paragraph 2 as follows.
Specializations of
weak_ptr
shall be CopyConstructible, CopyAssignable, and LessThanComparable, allowing their use in standard containers. Specializations ofweak_ptr
shall be convertible tobool
, allowing their use in boolean expressions and declarations in conditions. The template parameterT
ofweak_ptr
may be an incomplete type.
Howard Hinnant provided many helpful comments on drafts of this paper.