-
Notifications
You must be signed in to change notification settings - Fork 5.5k
C.65 Self-move - require only valid but unspecified #1938
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
Closed
Closed
Changes from 9 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
ff59df6
Update CppCoreGuidelines.md
bgloyer f211f4e
re-wording
bgloyer e0d2694
Merge branch 'isocpp:master' into C.65-self-move
bgloyer 864a9c6
Merge pull request #11 from bgloyer/master
bgloyer 0a5fa48
c.65 clean-up
bgloyer 67da273
wording clean-up
bgloyer 1a5c269
clean-up
bgloyer 63cf4d7
made hyphens more consistent
bgloyer 3863635
fix delete
bgloyer 839c4df
spelling
bgloyer a3ccaa3
revert C.64 change
bgloyer a9ac67a
reused m_owning
bgloyer 4ece32b
spelling
bgloyer eec4728
temp -> tmp
bgloyer b60a5db
incl. -> include
bgloyer 31d1433
remove trailing space
bgloyer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
|
@@ -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 | ||
// it requires a manual move-assignment | ||
}; | ||
|
||
Foo& Foo::operator=(Foo&& a) noexcept // OK, but there is a cost | ||
X& X::operator=(X&& a) noexcept | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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` | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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