Skip to content

Appendix C: Changes to "Define Copy, move, and destroy consistently" #2274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 12 additions & 73 deletions CppCoreGuidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -4878,6 +4878,8 @@ However, a programmer can disable or replace these defaults.

It's the simplest and gives the cleanest semantics.

Also, default'ed operations are classified by the compiler "trivial", which in turn allows for heavier optimization of the code using them (by the compiler, and in standard library implementations).

##### Example

struct Named_map {
Expand Down Expand Up @@ -22509,90 +22511,27 @@ When using exceptions as your error handling mechanism, always document this beh

**References**: [\[SuttAlex05\]](#SuttAlex05) Item 51; [\[C++03\]](#Cplusplus03) §15.2(3), §17.4.4.8(3), [\[Meyers96\]](#Meyers96) §11, [\[Stroustrup00\]](#Stroustrup00) §14.4.7, §E.2-4, [\[Sutter00\]](#Sutter00) §8, §16, [\[Sutter02\]](#Sutter02) §18-19

## <a name="Sd-consistent"></a>Define Copy, move, and destroy consistently

##### Reason

???
## <a name="Sd-consistent"></a>More on the "rule of five" & keeping copy, move, and destroy consistent

##### Note

If you define a copy constructor, you must also define a copy assignment operator.
In guideline [C.21](#c21-if-you-define-or-delete-any-copy-move-or-destructor-function-define-or-delete-them-all) it is mentioned that defining any of the copy operations prevents the implicit definition of the default move operation for the class. Even if your class itself does not have any special semantics for a move - its base class, or data members, may have different move semantics, which will not manifest in your class' copy operations - and this is a good reason to make the effort and implement even a 'trivial' move operation which only triggers moves of the base class and members.

##### Note

If you define a move constructor, you must also define a move assignment operator.

##### Example

class X {
public:
X(const X&) { /* stuff */ }

// BAD: failed to also define a copy assignment operator

X(x&&) noexcept { /* stuff */ }

// BAD: failed to also define a move assignment operator

// ...
};

X x1;
X x2 = x1; // ok
x2 = x1; // pitfall: either fails to compile, or does something suspicious

If you define a destructor, you should not use the compiler-generated copy or move operation; you probably need to define or suppress copy and/or move.

class X {
HANDLE hnd;
// ...
public:
~X() { /* custom stuff, such as closing hnd */ }
// suspicious: no mention of copying or moving -- what happens to hnd?
};

X x1;
X x2 = x1; // pitfall: either fails to compile, or does something suspicious
x2 = x1; // pitfall: either fails to compile, or does something suspicious

If you define copying, and any base or member has a type that defines a move operation, you should also define a move operation.

class X {
string s; // defines more efficient move operations
// ... other data members ...
public:
X(const X&) { /* stuff */ }
X& operator=(const X&) { /* stuff */ }

// BAD: failed to also define a move construction and move assignment
// (why wasn't the custom "stuff" repeated here?)
};

X test()
{
X local;
// ...
return local; // pitfall: will be inefficient and/or do the wrong thing
}

If you define any of the copy constructor, copy assignment operator, or destructor, you probably should define the others.

##### Note
If you need to define any of the five functions of the "rule of five" (see [C.21](#c21-if-you-define-or-delete-any-copy-move-or-destructor-function-define-or-delete-them-all)), it means you need it to do more than its default would mandate it -- and the (asymmetric) interrelation of the function implies you will need the same for more of the five functions. Here's how:

If you need to define any of these five functions, it means you need it to do more than its default behavior -- and the five are asymmetrically interrelated. Here's how:
* If you write/disable either of the *copy constructor* or the *copy assignment operator*, you probably need to do the same for the other one of these two: If one does "special" work, so should the other, probably, because the two functions should have similar effects. (See [\[SuttAlex05\]](#SuttAlex05) Item 52, which expands on this point in isolation.)
* If you explicitly write the two *copying functions*, you probably need to write the *destructor*: If the "special" work in the copy constructor is to allocate or duplicate some resource (e.g., memory, file, socket), you need to deallocate or release it in the destructor.
* If you explicitly write the *destructor*, you probably need to explicitly write or disable the *copying functions*: The non-trivial destructor often effects a release or de-allocation of a resource the object was holding; in this case, it is likely that those resources require careful duplication or allocation, and then you need to pay attention to the way objects are copied and assigned, or disable copying completely.

* If you write/disable either of the copy constructor or the copy assignment operator, you probably need to do the same for the other: If one does "special" work, probably so should the other because the two functions should have similar effects. (See Item 53, which expands on this point in isolation.)
* If you explicitly write the copying functions, you probably need to write the destructor: If the "special" work in the copy constructor is to allocate or duplicate some resource (e.g., memory, file, socket), you need to deallocate it in the destructor.
* If you explicitly write the destructor, you probably need to explicitly write or disable copying: If you have to write a non-trivial destructor, it's often because you need to manually release a resource that the object held. If so, it is likely that those resources require careful duplication, and then you need to pay attention to the way objects are copied and assigned, or disable copying completely.
In many cases, holding properly encapsulated resources using RAII "owning" objects can circumvent the need to write these operations yourself.

In many cases, holding properly encapsulated resources using RAII "owning" objects can eliminate the need to write these operations yourself. (See Item 13.)
**Exceptions**:

Prefer compiler-generated (including `=default`) special members; only these can be classified as "trivial", and at least one major standard library vendor heavily optimizes for classes having trivial special members. This is likely to become common practice.
* When any of the special functions are declared only to make them non-public or virtual, but without special semantics, it doesn't imply that the others are needed.
* Classes that have members of 'strange' types (such as reference members) may have peculiar copy or move semantics, while the construction or destruction behavior may not diverge from the language default.

**Exceptions**: When any of the special functions are declared only to make them non-public or virtual, but without special semantics, it doesn't imply that the others are needed.
In rare cases, classes that have members of strange types (such as reference members) are an exception because they have peculiar copy semantics.
In a class holding a reference, you likely need to write the copy constructor and the assignment operator, but the default destructor already does the right thing. (Note that using a reference member is almost always wrong.)

**References**: [\[SuttAlex05\]](#SuttAlex05) Item 52; [\[Cline99\]](#Cline99) §30.01-14, [\[Koenig97\]](#Koenig97) §4, [\[Stroustrup00\]](#Stroustrup00) §5.5, §10.4, [\[SuttHysl04b\]](#SuttHysl04b)

Expand Down
Loading