alfC
alfC

Reputation: 16242

Is it correct to resize a vector with moved-elements?

I am trying to understand the generic rules of move semantics. Specifically of containers and contained elements.

The reason is that I am trying to understand move in the context of ownership and iterator invalidation. To do that I am going through some cases with increasing complexity involving a typical container, a general contained type T, general g and f functions. (Maybe an important extra detail is that f might or might not actually do a move operation, or it could be contingent at runtime.)

The idea is to introduce Case 3 which is the core of this question.

Case 0

First a fairly uncontroversial case, this is ok:

std::vector<T> v(100, t);
f(std::move(v));
v = make_a_vector();

Case 1

However, use-after-move is likely smelly code

std::vector<T> v(100, t);
f(std::move(v));
g(v);

I think most people agree that this above is not ok. The rule being (as far I know) that the only operation after move should be assignment. I think this is particularly because it is undocumented (undefined but valid state) what is the state of a moved-from vector, (or even if it was moved at all.). So, at best v is empty and at worst v is an unspecified state and therefore g could do something unspecified in this scope.

Case 2:

std::vector<T> v(100, t);
f(std::move(v));
v.resize(120);

Is it correct? It is not an assignment, but resize has no preconditions. (found this Can I resize a vector that was moved from?)

Case 3:

Now the really tricky case.

std::vector<T> v(100);
h(std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));
v.resize(120);

(Here, h is a function that takes iterators, assume that implicitly it refers to the range [iterator1, iterator2).)

Is this correct code? The reason is that .resize seems to be going to play, move, swap and copy moved-from object of type T.

To summarize, is it correct to resize a vector whose elements have been (possibly) moved-from?


EDIT: For the sake of argument, let's specify the signatures of the functions in case they are relevant to the answer(s):

template<class T> void f(std::vector<T>&&); 
template<class T> void g(std::vector<T> const&);
template<class It> void h(It first, It last);

Upvotes: 1

Views: 131

Answers (2)

Jarod42
Jarod42

Reputation: 217283

std::vector<T> v(100, t);
f(std::move(v));
v.resize(120);

is similar to your case 1

std::vector<T> v(100, t);
f(std::move(v));
g(v);

vector::resize has no prerequires, but you don't know previous state. it might be empty, have some size (with unspecified value).

So after v.resize(120), you just know that the new size is 120, but doesn't know its contents (new elements are default constructed, but which are they?).

Upvotes: 1

M.M
M.M

Reputation: 141586

If it's a vector of standard-library objects, then it is guaranteed to be safe to resize the vector. See What constitutes a valid state for a "moved from" object in C++11? .

If a vector of your own objects -- then the question comes back to whether you designed your own objects to be valid once moved from.

Upvotes: 5

Related Questions