48

What does the C++11 standard say about self move assignment in relation to the standard library? To be more concrete, what, if anything, is guaranteed about what selfAssign does?

template<class T>
std::vector<T> selfAssign(std::vector<T> v) {
  v = std::move(v);
  return v;
}
T.C.
  • 123,516
  • 14
  • 264
  • 384
Bjarke H. Roune
  • 3,337
  • 2
  • 17
  • 24
  • 4
    @Mark I don't think this is a duplicate. That question is about writing your own. This question is about what the standard library guarantees. – R. Martinho Fernandes Oct 29 '12 at 19:02
  • 2
    Note that this does *not* involve the self-move-assignment of `T`. – Xeo Oct 29 '12 at 19:06
  • @Xeo I removed that line. Thanks for pointing out the error. – Bjarke H. Roune Oct 29 '12 at 19:21
  • 2
    voting to reopen as the marked "duplicate" does not cover guarantees on standard containers' move-assignment behaviour, and none of the answers provide Standard references like Howard Hinnant's answer to this thread – M.M Jul 07 '14 at 06:59

2 Answers2

38

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 anlvalue. —endnote]

So, the implementation of std::vector<T, A>::operator=(vector&& other) is allowed to assume that other is a prvalue. And if other is a prvalue, self-move-assignment is not possible.

What is likely to happen:

v will be left in a resource-less state (0 capacity). If v already has 0 capacity, then this will be 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 not t and rv refer to the same object.

Howard Hinnant
  • 179,402
  • 46
  • 391
  • 527
  • 6
    If I understand your quote correctly, the function `selfAssign` then invokes undefined behavior because the preconditions of the move assignment operator are not met. My understanding is that `std::swap` will do self move assignment on the call `std::swap(v, v)`. Does that make it undefined behavior to call `std::swap(v, v)`? – Bjarke H. Roune Oct 29 '12 at 19:11
  • Interesting. This would mean that `&&` implies `__restrict` for arguments then; I wonder if any compiler takes advantage of this yet. – Matthieu M. Oct 29 '12 at 19:25
  • 2
    @MatthieuM.: That's only what the standard says about *it's own* functions; that's not a general statement about `&&` parameters. You would not be restricted to that in code you write. – Nicol Bolas Oct 29 '12 at 19:31
  • @BjarkeH.Roune: swap(v, v) is legal and required to work. And it typically does because the self-move-assignment is done with a moved-from value (assuming we're not talking about vector which has a specialized swap which is also required to work with self-swap). In the generic self-swap, you're move assigning from an unspecified value to an unspecified value. So it really doesn't matter what happens as long as it doesn't crash. swap(t, t) requires `T` to be `MoveAssignable`. That requirement holds even if `T` is in a moved-from state. – Howard Hinnant Oct 29 '12 at 19:38
  • @HowardHinnant I'm not sure I understand your reasoning. I made the topic [a question](http://stackoverflow.com/questions/13129031/on-implementing-stdswap-in-terms-of-move-assignment-and-move-constructor). – Bjarke H. Roune Oct 29 '12 at 20:22
0

There is a relevant post by Eric Niebler with multiple links, e.g. to this answer by Howard Hinnant.

The latest С++20 working draft (N4861) is still kind of ambiguous on the matter for my taste. However, there is a recent Library Working Group issue 2839 which adds the following explicit statement at [lib.types.movedfrom]/2:

An object of a type defined in the C++ standard library may be move-assigned (11.4.6 [class.copy.assign]) to itself. Such an assignment places the object in a valid but unspecified state unless otherwise specified.

It was already in the N4885 working draft of C++23.

So, selfAssign is guaranteed to not cause undefined behavior and, as there are no extra guarantees for std::vector, leave v in some valid state.

yeputons
  • 5,711
  • 19
  • 50