Here is a possible definition of std::swap
:
template<class T>
void swap(T& a, T& b) {
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
}
I believe that
std::swap(v,v)
is guaranteed to have no effects andstd::swap
can be implemented as above.The following quote seems to me to imply that these beliefs are contradictory.
17.6.4.9 Function arguments [res.on.arguments]
1 Each of the following applies to all arguments to functions defined in the C++ standard library, unless explicitly stated otherwise.
...
- If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument. [ Note: If the parameter is a generic parameter of the form T&& and an lvalue of type A is bound, the argument binds to an lvalue reference (14.8.2.1) and thus is not covered by the previous sentence. — end note ] [ Note: If a program casts an lvalue to an xvalue while passing that lvalue to a library function (e.g. by calling the function with the argument move(x)), the program is effectively asking that function to treat that lvalue as a temporary. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. —endnote]
(thanks to Howard Hinnant for providing the quote)
Let v
be an object of some movable type taken from the Standard Template Library and consider the call std::swap(v, v)
. In the line a = std::move(b);
above, it is the case inside T::operator=(T&& t)
that this == &b
, so the parameter is not a unique reference. That is a violation of the requirement made above, so the line a = std::move(b)
invokes undefined behavior when called from std::swap(v, v)
.
What is the explanation here?
[res.on.arguments] is a statement about how the client should use the std::lib. When the client sends an xvalue to a std::lib function, the client has to be willing to pretend that the xvalue is really a prvalue, and expect the std::lib to take advantage of that.
However when the client calls std::swap(x, x), the client isn't sending an xvalue to a std::lib function. It is the implementation that is doing so instead. And so the onus is on the implementation to make std::swap(x, x) work.
That being said, the std has given the implementor a guarantee: X shall satisfy MoveAssignable
. Even if in a moved-from state, the client must ensure that X is MoveAssignable. Furthermore, the implementation of std::swap
doesn't really care what self-move-assignment does, as long as it is not undefined behavior for X. I.e. as long as it doesn't crash.
a = std::move(b);
When &a == &b, both the source and target of this assignment have an unspecified (moved-from) value. This can be a no-op, or it can do something else. As long as it doesn't crash, std::swap will work correctly. This is because in the next line:
b = std::move(tmp);
Whatever value went into a
from the previous line is going to be given a new value from tmp
. And tmp
has the original value of a
. So besides burning up a lot of cpu cycles, swap(a, a)
is a no-op.
Update
The latest working draft, N4618 has been modified to clearly state that in the MoveAssignable
requirements the expression:
t = rv
(where rv
is an rvalue), t
need only be the equivalent value of rv
prior to the assignment if t
and rv
do not reference the same object. And regardless, rv
's state is unspecified after the assignment. There is an additional note for further clarification:
rv
must still meet the requirements of the library component that is using it, whether or nott
andrv
refer to the same object.
Then the expression a = std::move(b);
gets executed, the object is already empty, in a state where only destruction is well defined. That will effectively be a no-op, as the object on the left and right hand sides is already empty. The state of the object after the move is still unknown but destructible. The next statement moves the contents back from tmp
and that sets the object back to a known state.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With