Skip to content
58 changes: 26 additions & 32 deletions CppCoreGuidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -6178,8 +6178,7 @@ After `y = std::move(x)` the value of `y` should be the value `x` had and `x` sh
int sz;
};


X::X(X&& a)
X::X(X&& a) noexcept
:p{a.p}, sz{a.sz} // steal representation
{
a.p = nullptr; // set to "empty"
Expand Down Expand Up @@ -6215,49 +6214,44 @@ Unless there is an exceptionally strong reason not to, make `x = std::move(y); y

##### Reason

If `x = x` changes the value of `x`, people will be surprised and bad errors can occur. However, people don't usually directly write a self-assignment that turn into a move, but it can occur. However, `std::swap` is implemented using move operations so if you accidentally do `swap(a, b)` where `a` and `b` refer to the same object, failing to handle self-move could be a serious and subtle error.
People don't usually directly write a self-move assignment, but it can occur. `std::swap` is implemented using
move operations so if you accidentally do `swap(a, b)` where `a` and `b` refer to the same object, failing to
handle self-move could be a serious and subtle error. At a minimum, a self-move should put the object into a
valid but unspecified state which is the same guarantee provided by the standard library containers. It is
not requred to leave the object unchanged.

##### Example

class Foo {
string s;
int i;
If all of the members of a type are safe for move-assignment, the default generated move-assignment
operator will be safe too.

X& operator=(X&& a) = default;

Otherwise, the manually written move-assignment operater must be made safe for self-assignement.

class X {
public:
Foo& operator=(Foo&& a);
X();
X& operator=(X&& a) noexcept;
// ...
~X() { delete owning_ptr; }
private:
T* owning_ptr; // bad (See R.20) but used in the example because
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't think of a simple example that required a manual move-assignment that didn't go against something in the guidelines

// it requires a manual move-assignment
};

Foo& Foo::operator=(Foo&& a) noexcept // OK, but there is a cost
X& X::operator=(X&& a) noexcept
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used 'X' instead of 'Foo' to match C.64

{
if (this == &a) return *this; // this line is redundant
s = std::move(a.s);
i = a.i;
auto* temp = a.owning_ptr;
a.owning_ptr = nullptr;
delete owning_ptr; // null in the case of a self move
owning_ptr = temp;
return *this;
}

The one-in-a-million argument against `if (this == &a) return *this;` tests from the discussion of [self-assignment](#Rc-copy-self) is even more relevant for self-move.

##### Note

There is no known general way of avoiding an `if (this == &a) return *this;` test for a move assignment and still get a correct answer (i.e., after `x = x` the value of `x` is unchanged).

##### Note

The ISO standard guarantees only a "valid but unspecified" state for the standard-library containers. Apparently this has not been a problem in about 10 years of experimental and production use. Please contact the editors if you find a counter example. The rule here is more caution and insists on complete safety.

##### Example

Here is a way to move a pointer without a test (imagine it as code in the implementation a move assignment):

// move from other.ptr to this->ptr
T* temp = other.ptr;
other.ptr = nullptr;
delete ptr;
ptr = temp;

##### Enforcement

* (Moderate) In the case of self-assignment, a move assignment operator should not leave the object holding pointer members that have been `delete`d or set to `nullptr`.
* (Moderate) In the case of self-assignment, a move assignment operator should not leave the object holding pointer members that have been deleted.
* (Not enforceable) Look at the use of standard-library container types (incl. `string`) and consider them safe for ordinary (not life-critical) uses.

### <a name="Rc-move-noexcept"></a>C.66: Make move operations `noexcept`
Expand Down