A very simple question but I couldn't find an answer. It would make awful lot of sense for it to be allowed but want to double-check.
std::vector<int> v(10, 0);
v.erase(v.end()); // allowed or not?
An invalid position or range causes undefined behavior.
From here
The iterator pos must be valid and dereferenceable. Thus the end()
iterator (which is valid, but is not dereferencable) cannot be used as
a value for pos.
For the single argument overload it is NOT valid to pass end() to std::vector::erase, because the single argument overload will erase the element AT that position. There is no element at the end() position, since end() is one past the last element.
However, end() can be passed to the overload of erase that takes an Iterator range:
vec.erase(vec.begin(), vec.end())
Related
I have a question in iterators about the difference between begin() and rend().
#include <iostream>
#include <array>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> v1;
v1 = {9,2,6,4,5};
cout<<*v1.begin();
cout<<*v1.rend();
return 0;
}
cout<<*v1.begin();
returns 9
but
cout<<*v1.rend();
returns a number that is not 9
Why are there such different results?
In C++, ranges are marked by a pair of iterators marking the beginning of the range and a position one past the end of the range. For containers, the begin() and end() member functions provide you with a pair of iterators to the first and past-the-end positions. It’s not safe to read from end(), since it doesn’t point to an actual element.
Similarly, the rbegin() and rend() member functions return reverse iterators that point, respectively, to the last and just-before-the-first positions. For the same reason that it’s not safe to dereference the end() iterator (it’s past the end of the range), you shouldn’t dereference the rend() iterator, since it doesn’t point to an element within the container.
Dereferencing end() or rend() has undefined behaviour.
begin() points to the first element, rbegin() points to the last element. end() (in most cases) points to one after the last element and rend() effectively points to one before the first element (though it isn't implemented like that).
What is the difference between begin () and rend ()?
begin returns an iterator to the first element of the container.
rend returns a reverse iterator to one before the first element of the container (which is one past the last element in the reverse iterator range).
*v1.rend()
The behaviour of indirecting through the rend iterator is undefined (same goes for indirecting through end iterator).
Why are there such different results?
Besides behaviour being undefined in one case and not the other, since they refer to different to different elements (one of them being an element that doesn't exist), there is little reason to assume the results to be the same.
vector::rend() is a built-in function in the C++ standard library which returns a reverse iterator pointing to the theoretical element right before the first element in the array container. but vector::begin() returns an iterator pointing to the first element in the vector.
See this code :
for (auto it = v1.rbegin(); it != v1.rend(); it++)
cout << *it << " ";
The vector elements in reverse order are :
5 4 6 2 9
To iterate in the vector always choose one of these methods together :
vector::begin() and vector::end()
vector::cbegin() and vector::cend()
vector::crbegin() and vector::crend()
vector::rbegin() and vector::rend()
For more information see "C++ Vector Tutorial With Example" by Ankit Lathiya.
Try it online
I prefer to get info from the source, for this case this is ISO-IEC 14882,
where erase method is described as the following:
"iterator erase(const_iterator position);
iterator erase(const_iterator first, const_iterator last);
Effects: Invalidates
iterators and references at or after the point of the erase.
Complexity: The destructor of T is called the number of times equal to
the number of the elements erased, but the move assignment operator of
T is called the number of times equal to the number of elements in the
vector after the erased elements.
Throws: Nothing unless an exception
is thrown by the copy constructor, move constructor, assignment
operator, or move assignment operator of T."
Can't find information about returned iterator,
of course, I Googled and got:
An iterator pointing to the new location of the element that followed
the last element erased by the function call
Can't understand there this is described in the standard
Could you point me on it?
Update: my question is not about how vector::erase works,
but rather from where in the standard, as I accept as reliable source of information we can deduce information about returned value
The information is in a slightly un-intuitive place. What erase returns is detailed under the sequence containers section of general container requirements section, specificly [sequence.reqmts]/11
The iterator returned from a.erase(q) points to the element immediately following q prior to the element being erased. If no such element exists, a.end() is returned.
and [sequence.reqmts]/12
The iterator returned by a.erase(q1, q2) points to the element pointed to by q2 prior to any elements being erased. If no such element exists, a.end() is returned.
I have a map definition and subsequent manipulation like this.
map<int,string> m;
m.insert(std::pair<int,string>(1,"A");
m.insert(std::pair<int,string>(2,"B");
m.insert(std::pair<int,string>(3,"C");
m.insert(std::pair<int,string>(4,"D");
auto it = m.find(2);
m.erase(m.find(3));
cout<< it->second;
Will "it" be valid after an erase to some other element ?
Will "it" be valid after an erase to some other element ?
Yes, std::map::erase will only invalidate references and iterators to the erased elements.
References and iterators to the erased elements are invalidated. Other references and iterators are not affected.
Note the code m.erase(m.find(3)); has a potential problem, since std::map::find will return end() iterator if nothing is found, but end() iterator cannot be used with std::map::erase.
The iterator pos must be valid and dereferenceable. Thus the end()
iterator (which is valid, but is not dereferencable) cannot be used as
a value for pos.
Yes it will. Only the erased iterator is invalidated when a map erase is performed.
As per documentation, std::find returns
last
if no element is found. What does that mean? Does it return an iterator pointing to the last element in the container? Or does it return an iterator pointing to .end(), i.e. pointing outside the container?
The following code prints 0, which is not an element of the container. So, I guess std::find returns an iterator outside the container. Could you please confirm?
int main()
{
vector<int> vec = {1, 2,3, 1000, 4, 5};
auto itr = std::find(vec.begin(), vec.end(), 456);
cout << *itr;
}
last is the name of second parameter to find. It doesn't know what kind of container you're using, just the iterators that you give it.
In your example, last is vec.end(), which is (by definition) not dereferenceable, since it's one past the last element. So by dereferencing it, you invoke undefined behaviour, which in this case manifests as printing out 0.
Algorithms apply to ranges, which are defined by a pair of iterators. Those iterators are passed as arguments to the algorithm. The first iterator points at the first element in the range, and the second argument points at one past the end of the range. Algorithms that can fail return a copy of the past-the-end iterator when they fail. That's what std::find does: if there is no matching element it returns its second argument.
Note that the preceding paragraph does not use the word "container". Containers have member functions that give you a range that you can use to get at the elements of the container, but there are also ways of creating iterators that have no connection to any container.
Based on this documentation, it literally says:
"Return value:
Iterator to the first element satisfying the condition or last if no such element is found."
In your case, it's out the vector by one, .end()
I need to remove an element from a std::list after finding it with std::find. What is the behavior of calling std::list::erase with the end() of the list? My case is something like this:
std::list<T> mylist;
T value;
std::list::iterator it = std::find(mylist.begin(), mylist.end(), value);
std::list::iterator next = mylist.erase(it);
cplusplus.com says:
If position (or the range) is valid, the function never throws exceptions (no-throw guarantee).
Otherwise, it causes undefined behavior.
but what I don't know is whether end() is considered valid there.
That site uses the vague (and arguably incorrect) term "valid", but the library specification (C++11 23.2.3) uses the more specific term "dereferenceable" - meaning that the iterator must refer to an object. The past-the-end iterator is not dereferenceable, so erasing it gives undefined behaviour.
It isn't. Trying to erase end() results in undefined behaviour.
end() Returns an iterator referring to the past-the-end iterator in the list container and not the last object in the list.
By deleting/erasing end, you are deleting outside the range of your list. Your code should be:
std::list<T> mylist;
T value;
std::list::iterator it = std::find(mylist.begin(), mylist.end(), value);
If(it!=mylist.end())
std::list::iterator next = mylist.erase(it);
Also if find() fails to find a value in your list it will return the end iterator, it is basically telling you that the value you are searching for is outside of your list (not in you list)