Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::vector<T>::assign using a subrange valid?

I want to convert a vector into a sub-range of that vector, for example by removing the first and last values. Is the use of the assign member function valid in this context?

std::vector<int> data = {1, 2, 3, 4};
data.assign(data.begin() + 1, data.end() - 1);
// data is hopefully {2, 3}

Cppreference states that

All iterators, pointers and references to the elements of the container are invalidated. The past-the-end iterator is also invalidated.

This invalidation, however, doesn't appear to happen until the end of assign.

To be safe, I could just go with the following but it seems more verbose:

std::vector<int> data = {1, 2, 3, 4};
data = std::vector<int>{data.begin() + 1, data.end() - 1};
// data is now {2, 3}
like image 416
dfreese Avatar asked Sep 26 '19 16:09

dfreese


1 Answers

The __invalidate_all_iterators function that your link refers to is merely a debugging tool. It doesn't "cause" the iterators to be invalidated; It effectively reports that the iterators have been invalidated by the previous actions. It may be that this debugging tool might not catch a bug caused by this assignment.

It is a precondition of assign that the iterators are not to the same container. A precondition violation results in undefined behaviour.

Standard quote (latest draft):

[sequence.reqmts] a.assign(i,j) Expects: T is Cpp17EmplaceConstructible into X from *i and assignable from *i. For vector, if the iterator does not meet the forward iterator requirements ([forward.iterators]), T is also Cpp17MoveInsertable into X. Neither i nor j are iterators into a.

Your safe alternative is correct.

If you want to avoid reallocation (keeping in mind that there will be unused space left), and if you want to avoid copying (which is important for complex types, doesn't matter for int), then following should be efficient:

int begin_offset = 1;
int end_offset = 1;
if (begin_offset)
    std::move(data.begin() + begin_offset, data.end() - end_offset, data.begin());
data.erase(data.end() - end_offset - begin_offset, data.end());
like image 185
eerorika Avatar answered Oct 11 '22 04:10

eerorika