2

Looking at std::remove reference I see:

template< class ForwardIt, class T >
ForwardIt remove( ForwardIt first, ForwardIt last, const T& value );

Removing is done by shifting (by means of move assignment) the elements in the range. Iterators pointing to an element between the new logical end and the physical end of the range are still dereferenceable, but the elements themselves have unspecified values (as per MoveAssignable post-condition).

So, I see MoveAssignable conept:

t = rv   // Post-condition:The new value of rv is unspecified.

So far, it is OK. Question about MoveAssignable concept:

std::string s1 = "abc";
std::string s2;
s2 = std::move(s1);
std::cout << s1;    // is this code valid?
s1 = "cde";         // is this code valid?

Can I reuse "moved from" variable by reading its value or by re-assigning its value?

Now I am looking at std::move reference and it looks a bit surprising:

template< class InputIt, class OutputIt >
OutputIt move( InputIt first, InputIt last, OutputIt d_first );

Moves the elements in the range [first, last), to another range beginning at d_first. After this operation the elements in the moved-from range will still contain valid values of the appropriate type, but not necessarily the same values as before the move.

So, moved-from elements are defined by different way in std::remove and std::move, though they should be the same. Which one is correct? What are the rules of reusing (or not using) of moved-from elements?

Alex F
  • 39,172
  • 34
  • 138
  • 200
  • 3
    The general rule is that you can call all member functions that don't have any preconditions. Since the value of the moved-from object is unspecified, you can obviously not know whether the object satisfies any preconditions. The only operations you would use in production code are assignment and destruction, though, unless a class makes special additional promises (such as unique_ptr). – Kerrek SB Jun 26 '17 at 12:09
  • 1
    Your quotes mean the same thing. They both boil down to you having a valid object with a unspecified value. Your first question is answered [here](https://stackoverflow.com/questions/12095048/what-constitutes-a-valid-state-for-a-moved-from-object-in-c11) and [here](https://stackoverflow.com/questions/7930105/does-moving-leave-the-object-in-a-usable-state) – NathanOliver Jun 26 '17 at 12:27
  • Thanks, got both points about MoveAssignable and different algorithm definitions. – Alex F Jun 26 '17 at 12:33

1 Answers1

1

The actual wording in the Standard is found in section [lib.types.movedfrom]:

Objects of types defined in the C++ standard library may be moved from ([class.copy]). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.

The description of std::move ([alg.move]) simply says that elements are moved and makes no comment about the moved-from objects. The description of std::remove ([alg.remove]) has a non-normative note:

Note: each element in the range [ret,last), where ret is the returned value, has a valid but unspecified state, because the algorithms can eliminate elements by moving from elements that were originally in that range.

So cppreference happens to use different phrasing on those two pages, but they mean exactly the same thing.

Now, given that std::string s1 has been moved from, is it valid to do

std::cout << s1;

Yes, it's allowed because s1 is "valid", but it's not useful, since you have no guarantees on what will be output. On the other hand,

s1 = "cde";

is both safe and useful, since any previous contents of s1 are discarded.

For an example of "unless otherwise specified", take a look at the move assignment operator of std::unique_ptr<T> ([unique.ptr.single.asgn]):

unique_ptr& operator=(unique_ptr&& u) noexcept;

Effects: Transfers ownership from u to *this as if by calling reset(u.release()) followed by get_deleter() = std::forward<D>(u.get_deleter()).

Since u.release() leaves u empty/null, we're actually guaranteed the moved-from object will be empty/null. Similar guarantees are made for the move constructor and the templated constructor and assignment taking a unique_ptr with different template argument, so that a moved-from unique_ptr is always empty/null.

aschepler
  • 65,919
  • 8
  • 93
  • 144