moving elements in same container - c++

I'm trying to get information if below code is undefined behavior, I did read here.
In particular I want to be sure that iterator variable iter will be valid in cases where moved elements overlap destination range.
I think this is not good but can't confirm, and what would be better way to do this?
#include <vector>
#include <iostream>
int main()
{
// move 3, 4 closer to beginning
std::vector<int> vec { 1, 2, 3, 4, 5, 6, 7 };
auto iter = std::move(vec.begin() + 2, vec.end() - 3, vec.begin() + 1);
std::cout << *iter << std::endl;
}
also there is no reference to what this iter is supposed to point at?

Yes it's well-formed to use std::move for this case.
(emphasis mine)
When moving overlapping ranges, std::move is appropriate when moving
to the left (beginning of the destination range is outside the source
range) while std::move_backward is appropriate when moving to the
right (end of the destination range is outside the source range).
And about the return value,
Output iterator to the element past the last element moved (d_first + (last - first))
So iter points to the element following the last element moved, i.e.
// elements of vec (after moved)
// 1, 3, 4, x, 5, 6, 7
// here ^
Note that
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.
Here, x is such an unspecified value. This will probably happen to be another 4, but this shouldn't be relied upon. Subtle changes, such as a vector of strings instead of numbers, can cause different behavior. For example, the same operation on { "one", "two", "three", "four", "five", "six", "seven" } would probably result in it being "two" rather than the "four" you might expect from seeing the numeric example.

Related

What is std::views::counted?

On https://en.cppreference.com/w/cpp/ranges, std::views::counted is listed in the range adaptors section. However, it is not tagged as range adaptor object.
I guess that why I can't write using the pipe operator like:
std::vector<size_t> vec = {1, 2, 3, 4, 5};
auto view = vec | std::ranges::counted(... ; // does not compile
My questions are:
what is a std::ranges::counted? Why is it listed in the range adaptor section?
what are the use cases? what are the advantages over using take and drop?
Cppreference is following the organization of the C++20 standard. And it puts views::counted into the "Range Adaptors" section. Despite the fact that the standard says:
These adaptors can be chained to create pipelines of range transformations that evaluate lazily as the resulting view is iterated.
This is not true of the behavior of views::counted. Indeed, most of the other elements in that section say that their customization points "denotes a range adaptor object" (which describes the piping functionality), but views::counted does not.
It's unclear why they put it in that section, but it is a useful type in-and-of itself. It's really just an efficient way of saying subrange(it, it + n). It is efficient in that it doesn't actually increment the iterator by n.
The advantage it has over take_view is that take_view operates on a range, while all counted needs is an iterator. The main difference is that counted assumes that there are n valid iterator positions (and will give UB if that is not the case), while take_view does not. take_view will give you up to n objects, but if the range is shorter than that (as defined by the sentinel), it doesn't try to iterate past the end of the range.
According to the docs
A counted view presents a view of the elements of the counted range [i, n) for some iterator i and non-negative integer n.
A counted range [i, n) is the n elements starting with the element pointed to by i and up to but not including the element, if any, pointed to by the result of n applications of ++i.
So essentially it returns a slice given a starting iterator and a number of elements to include after that iterator. The example shown in the docs is
#include <ranges>
#include <iostream>
int main()
{
const int a[] = {1, 2, 3, 4, 5, 6, 7};
for(int i : std::views::counted(a, 3))
std::cout << i << ' ';
std::cout << '\n';
const auto il = {1, 2, 3, 4, 5};
for (int i : std::views::counted(il.begin() + 1, 3))
std::cout << i << ' ';
std::cout << '\n';
}
Output
1 2 3
2 3 4
Comparing the specific functions you listed, here are their summaries:
std::ranges::views::take: a view consisting of (up to) the first N elements of another view
std::ranges::views::drop: a view consisting of elements of another view, skipping (up to) the first N elements
std::ranges::views::counted: creates a subrange from an iterator and a count, always containing exactly N elements
views::counted is similar to views::take. However, the former accepts an iterator instead of a range.
One of the benefits of views::counted over views::take is that it allows us to construct a sized input/output range when we already know its size:
auto ints = views::istream<int>(std::cin);
auto counted = views::counted(ints.begin(), 4);
auto take = views::take(ints, 4);
static_assert(ranges::sized_range<decltype(counted)>); // ok
static_assert(ranges::sized_range<decltype(take)>); // failed
Unlike views::take, since we omit the sentinel information, we must ensure that the iterator is still valid after incrementing n times, while the former is guaranteed to always return a valid range.

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}
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());

How these lines execute [duplicate]

int main()
{
const int SIZE = 10;
int a[SIZE] = {10, 2, 35, 5, 10, 26, 67, 2, 5, 10};
std::ostream_iterator< int > output(cout, " ");
std::vector< int > v(a, a + SIZE);
std::vector< int >::iterator newLastElement;
cout << "contents of the vector: ";
std::copy(v.begin(), v.end(), output);
newLastElement = std::remove(v.begin(), v.end(), 10);
cout << "\ncontents of the vector after remove: ";
//std::copy(v.begin(), newLastElement, output);
//this gives the correct result : 2 35 5 26 67 2 5
std::copy(v.begin(), v.end(), output);
//this gives a 10 which was supposed to be removed : 2 35 5 26 67 2 5 2 5 10
cout << endl;
return 0;
}
There are three 10 in the array a.
why does the array v contains a 10 after we remove the all the 10s with remove function.
you can see the compiled output also here
Actually std::remove doesn't remove the item from the container. Quoted from here
Remove removes from the range [first, last) all elements that are equal to value. That is, remove returns an iterator new_last such that the range [first, new_last) contains no elements equal to value. The iterators in the range [new_last, last) are all still dereferenceable, but the elements that they point to are unspecified. Remove is stable, meaning that the relative order of elements that are not equal to value is unchanged.`
That is, std::remove works with a pair of iterators only and does not know anything about the container which actually contains the items. In fact, it's not possible for std::remove to know the underlying container, because there is no way it can go from a pair of iterators to discover about the container to which the iterators belong. So std::remove doesn't really remove the items, simply because it cannot. The only way to actually remove an item from a container is to invoke a member function on that container.
So if you want to remove the items, then use Erase-Remove Idiom:
v.erase(std::remove(v.begin(), v.end(), 10), v.end());
The erase-remove idiom is so common and useful is that std::list has added another member function called list::remove which produces the same effect as that of the erase-remove idiom.
std::list<int> l;
//...
l.remove(10); //it "actually" removes all elements with value 10!
That means, you don't need to use erase-remove idiom when you work with std::list. You can directly call its member function list::remove.
The reason is that STL algorithms do not modify the size of the sequence. remove, instead of actually erasing items, moves them and returns an iterator to the "new" end. That iterator can then be passed to the erase member function of your container to actually perform the removal:
v.erase(std::remove(v.begin(), v.end(), 10), v.end());
By the way, this is known as the "erase-remove idiom".
EDIT: I was incorrect. See comments, and Nawaz's answer.
Because std::remove doesn't actually shrink the container, it just moves all the elements down to to fill up the spot used by the "removed" element. For example, if you have a sequence 1 2 3 4 5 and use std::remove to remove the value 2, your sequence will look like 1 3 4 5 5. If you then remove the value 4, you'll get 1 3 5 5 5. At no point does the sequence ever get told to be shorter.
C++20 introduces a new non-member function std::erase that simplifies this task for all standard library containers.
The solution suggested by multiple older answers here:
v.erase(std::remove(v.begin(), v.end(), 10), v.end());
Can now be written as:
std::erase(v, 10);

STL remove doesn't work as expected?

int main()
{
const int SIZE = 10;
int a[SIZE] = {10, 2, 35, 5, 10, 26, 67, 2, 5, 10};
std::ostream_iterator< int > output(cout, " ");
std::vector< int > v(a, a + SIZE);
std::vector< int >::iterator newLastElement;
cout << "contents of the vector: ";
std::copy(v.begin(), v.end(), output);
newLastElement = std::remove(v.begin(), v.end(), 10);
cout << "\ncontents of the vector after remove: ";
//std::copy(v.begin(), newLastElement, output);
//this gives the correct result : 2 35 5 26 67 2 5
std::copy(v.begin(), v.end(), output);
//this gives a 10 which was supposed to be removed : 2 35 5 26 67 2 5 2 5 10
cout << endl;
return 0;
}
There are three 10 in the array a.
why does the array v contains a 10 after we remove the all the 10s with remove function.
you can see the compiled output also here
Actually std::remove doesn't remove the item from the container. Quoted from here
Remove removes from the range [first, last) all elements that are equal to value. That is, remove returns an iterator new_last such that the range [first, new_last) contains no elements equal to value. The iterators in the range [new_last, last) are all still dereferenceable, but the elements that they point to are unspecified. Remove is stable, meaning that the relative order of elements that are not equal to value is unchanged.`
That is, std::remove works with a pair of iterators only and does not know anything about the container which actually contains the items. In fact, it's not possible for std::remove to know the underlying container, because there is no way it can go from a pair of iterators to discover about the container to which the iterators belong. So std::remove doesn't really remove the items, simply because it cannot. The only way to actually remove an item from a container is to invoke a member function on that container.
So if you want to remove the items, then use Erase-Remove Idiom:
v.erase(std::remove(v.begin(), v.end(), 10), v.end());
The erase-remove idiom is so common and useful is that std::list has added another member function called list::remove which produces the same effect as that of the erase-remove idiom.
std::list<int> l;
//...
l.remove(10); //it "actually" removes all elements with value 10!
That means, you don't need to use erase-remove idiom when you work with std::list. You can directly call its member function list::remove.
The reason is that STL algorithms do not modify the size of the sequence. remove, instead of actually erasing items, moves them and returns an iterator to the "new" end. That iterator can then be passed to the erase member function of your container to actually perform the removal:
v.erase(std::remove(v.begin(), v.end(), 10), v.end());
By the way, this is known as the "erase-remove idiom".
EDIT: I was incorrect. See comments, and Nawaz's answer.
Because std::remove doesn't actually shrink the container, it just moves all the elements down to to fill up the spot used by the "removed" element. For example, if you have a sequence 1 2 3 4 5 and use std::remove to remove the value 2, your sequence will look like 1 3 4 5 5. If you then remove the value 4, you'll get 1 3 5 5 5. At no point does the sequence ever get told to be shorter.
C++20 introduces a new non-member function std::erase that simplifies this task for all standard library containers.
The solution suggested by multiple older answers here:
v.erase(std::remove(v.begin(), v.end(), 10), v.end());
Can now be written as:
std::erase(v, 10);

stl insertion iterators

Maybe I am missing something completely obvious, but I can't figure out why one would use back_inserter/front_inserter/inserter,
instead of just providing the appropriate iterator from the container interface.
And thats my question.
Because those call push_back, push_front, and insert which the container's "normal" iterators can't (or, at least, don't).
Example:
int main() {
using namespace std;
vector<int> a (3, 42), b;
copy(a.begin(), a.end(), back_inserter(b));
copy(b.rbegin(), b.rend(), ostream_iterator<int>(cout, ", "));
return 0;
}
The main reason is that regular iterators iterate over existing elements in the container, while the *inserter family of iterators actually inserts new elements in the container.
std::vector<int> v(3); // { 0, 0, 0 }
int array[] = { 1, 2, 3 };
std::copy( array, array+3, std::back_inserter(v) ); // adds 3 elements
// v = { 0, 0, 0, 1, 2, 3 }
std::copy( array, array+3, v.begin() ); // overwrites 3 elements
// v = { 1, 2, 3, 1, 2, 3 }
int array2[] = { 4, 5, 6 };
std::copy( array2, array2+3, std::inserter(v, v.begin()) );
// v = { 4, 5, 6, 1, 2, 3, 1, 2, 3 }
The iterator points to an element, and doesn't in general know what container it's attached to. (Iterators were based on pointers, and you can't tell from a pointer what data structure it's associated with.)
Adding an element to an STL container changes the description of the container. For example, STL containers have a .size() function, which has to change. Since some metadata has to change, whatever inserts the new element has to know what container it's adding to.
It is all a matter of what you really need. Both kinds of iterators can be used, depending on your intent.
When you use an "ordinary" iterator, it does not create new elements in the container. It simply writes the data into the existing consecutive elements of the container, one after another. It overwrites any data that is already in the container. And if it is allowed to reach the end of the sequence, any further writes make it "fall off the end" and cause undefined behavior. I.e. it crashes.
Inserter iterators, on the other hand, create a new element and insert it at the current position (front, back, somewhere in the middle) every time something is written through them. They never overwrite existing elements, they add new ones.