iterators can't access problems properly - c++

I am trying to access the element of a vector using the iterator. But I get strange outputs.
std::vector<int> ivec{ 7, 6 , 8, 9} ;
std::vector<int>::iterator beg = ivec.begin();
std::vector<int>::iterator last = ivec.end();
std::cout << *beg << *last << std::endl;
But, In the above case the program shows me error: debug assertion failed. Vector iterator not dereferencable. and this error is particularly for *last. If I just print *beg that seems to wrong fine. But can't dereference the last one.
Other problem I get with iterator is during increment.
std::vector<int>::iterator beg = ivec.begin();
cout << *(beg++) ; // in this case it prints me value of 7
cout << *(++beg) ; // in this case it print me the right value of second place i.e. is 6
cout << *(beg+=1) ; // in this case we also print the second value i.e. 6

end iterators are not iterators that you can de-reference. They point past the last element into the container. There's a good reason why this needs to be true; but long and short, the end iterator does not actually point to any element. If you want the last element, you need to decrement the end iterator.

For your first example, std::vector<T>::end points to the theoretical element after the actual last element, so dereferencing it does not make sense. It is primarily used for checking when you have got past the end of the vector in a loop.
For your second example, the results are as you would expect:
cout << *(beg++) ; //increments beg after dereferencing
cout << *(++beg) ; //increments beg before dereferencing
cout << *(beg+=1) ; //dereferences the result of adding one to beg

As it is stated in here,
Return iterator to end, Returns an iterator referring to the past-the-end element in the vector container.
The past-the-end element is the theoretical element that would follow the last element in the vector. It does not point to any element, and thus shall not be dereferenced.
Because the ranges used by functions of the standard library do not include the element pointed by their closing iterator, this function is often used in combination with vector::begin to specify a range including all the elements in the container.
If the container is empty, this function returns the same as vector::begin.

Related

What is the list::insert behavior when the informed iterator argument is initialized to the begining of an empty list?

Suppose you have a C++ empty list:
list<int> l;
and you insert three new elements from the beginning:
auto it = l.begin();
l.insert(it,10);
l.insert(it,20);
l.insert(it,30);
when trying to print the list elements from the beginning to the end:
for(int i: l){
cout << i << ' ';
}
the obtained result is: 10 20 30.
But it is supposed that insert function inserts elements before the element pointed by the iterator, so the obtained result should have been: 30 20 10.
Why does this happen?
When the list is empty, the begin() iterator compares equal to the end() iterator. Calling insert() with the end() iterator inserts the value at the end of the list. insert() does not invalidate any iterators, so your it variable is still holding the end() iterator each time you are calling insert().
If you want your values to be in the reverse order that you call insert(), use the iterator that insert() returns to you, eg:
auto it = l.begin();
it = l.insert(it,10);
it = l.insert(it,20);
it = l.insert(it,30);
Live Demo

remove algorithm unexpected behaviour [duplicate]

I am bit confused about the difference between the usage of std::remove algorithm. Specifically I am not able to understand what is being removed when I use this algorithm. I wrote a small test code like this:
std::vector<int> a;
a.push_back(1);
a.push_back(2);
std::remove(a.begin(), a.end(), 1);
int s = a.size();
std::vector<int>::iterator iter = a.begin();
std::vector<int>::iterator endIter = a.end();
std::cout<<"Using iter...\n";
for(; iter != endIter; ++iter)
{
std::cout<<*iter<<"\n";
}
std::cout<<"Using size...\n";
for(int i = 0; i < a.size(); ++i)
{
std::cout<<a[i]<<"\n";
}
The output was 2,2 in both the cases.
However, if I use erase with the remove something like this:
a.erase(std::remove(a.begin(), a.end(), 1), a.end());
I get the output as 2.
So my questions are:
(1). Is there any use of std::remove other than using it with erase function.
(2). Even after doing std::remove, why a.size() returns 2 and not 1?
I read the item in Scott Meyer's Effective STL book about the erase-remove idiom. But am still having this confusion.
remove() doesn't actually delete elements from the container -- it only shunts non-deleted elements forwards on top of deleted elements. The key is to realise that remove() is designed to work on not just a container but on any arbitrary forward iterator pair: that means it can't actually delete the elements, because an arbitrary iterator pair doesn't necessarily have the ability to delete elements.
For example, pointers to the beginning and end of a regular C array are forward iterators and as such can be used with remove():
int foo[100];
...
remove(foo, foo + 100, 42); // Remove all elements equal to 42
Here it's obvious that remove() cannot resize the array!
What does std::remove do?
Here's pseudo code of std::remove. Take few seconds to see what it's doing and then read the explanation.
Iter remove(Iter start, Iter end, T val) {
Iter destination = start;
//loop through entire list
while(start != end) {
//skip element(s) to be removed
if (*start == val) {
start++;
}
else //retain rest of the elements
*destination++ = *start++;
}
//return the new end of the list
return destination;
}
Notice that remove simply moved up the elements in the sequence, overwriting the values that you wanted to remove. So the values you wanted to remove are indeed gone, but then what's the problem? Let say you had vector with values {1, 2, 3, 4, 5}. After you call remove for val = 3, the vector now has {1, 2, 4, 5, 5}. That is, 4 and 5 got moved up so that 3 is gone from the vector but the size of vector hasn't changed. Also, the end of the vector now contains additional left over copy of 5.
What does vector::erase do?
std::erase takes start and end of the range you want to get rid off. It does not take the value you want to remove, only start and end of the range. Here's pseudo code for how it works:
erase(Iter first, Iter last)
{
//copy remaining elements from last
while (last != end())
*first++ = *last++;
//truncate vector
resize(first - begin());
}
So the erase operation actually changes the size of container and frees up the memory.
The remove-erase idiom
The combination of std::remove and std::erase allows you to remove matching elements from the container so that container would actually get truncated if elements were removed. Here's how to do it:
//first do the remove
auto removed = std::remove(vec.begin(), vec.end(), val);
//now truncate the vector
vec.erase(removed, vec.end());
This is known as the remove-erase idiom. Why is it designed like this? The insight is that the operation of finding elements is more generic and independent of underlying container (only dependent on iterators). However operation of erase depends on how container is storing memory (for example, you might have linked list instead of dynamic array). So STL expects containers to do its own erase while providing generic "remove" operation so all containers don't have to implement that code. In my view, the name is very misleading and std::remove should have been called std::find_move.
Note: Above code is strictly pseudocode. The actual STL implementation is more smarter, for example, using std::move instead of copy.
std::remove does not remove the actual objects, rather, pushes them to the end of the container. Actual deletion and deallocation of memory is done via erase. So:
(1). Is there any use of std::remove other than using it with erase function.
Yes, it helps to get a pair of iterators to a new sequence without having worry about proper de-allocation etc.
(2). Even after doing std::remove, why a.size() returns 2 and not 1?
The container still holds to those objects, you only have a new set of iterators to work with. Hence the size is still what it used to be.
i faced the same issue, trying to understand the difference.
the explanations that have been give so far are right on the money, but i only understood them after seeing an example;
#include <algorithm>
#include <string>
#include <iostream>
#include <cctype>
int main()
{
std::string str1 = "Text with some spaces";
std::string::iterator it = remove(str1.begin(), str1.end(), 't');
std::cout << str1 << std::endl;// prints "Tex wih some spaceses"
for (str1.begin();it != str1.end(); ++it)
{
std::cout << *it; //prints "es"
}
}
as you can see, the remove, only moves the lower case 't' to the end of the string, while returning a new iterator to the end of the new string (new string is the old string up to where the removed element are inserted)
this is why when you print the iterator that you got from "remove"
"Text with some spaces"
^ ^removes both 't', then shift all elements forward -1 //what we want to remove
"Text with some spaces"
^ end of string -2 //original state of string
"Tex with some spacess"
^end of string -3 //first 't' removed
"Tex wih some spaceses"
^end of string -4 //second 't' removed
"Tex wih some spaceses"
^new iterator that remove() returned -5 // the state of string after "remove" and without "erase"
if you pass the iterator you obtained from step 5 to "erase()" it will know to erase from there to the end of string re-sizing the string in process
To remove element with some condition(equal some value or other condition like less than) in container like vector, it always combine function member function erase and std::remove or std::remove_if.
In vector, the function erase can just delete element by position, like:
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
But if you want to erase elements with some condition, you can combine it with std::remove or std::remove_if.
For example, you want to erase all the elements 6 in the below vector:
std::vector<int> vec{6, 8, 10, 3, 4, 5, 6, 6, 6, 7, 8};
// std::remove move elements and return iterator for vector erase funtion
auto last = std::remove(vec.begin(), vec.end(), 6);
for(int a:vec)
cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8 6 6 7 8
vec.erase(last, vec.end());
for(int a:vec)
cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8
std::remove works as below, it does't erase any elements, it just move elements and returns the iterator.
Possible implementation:
template< class ForwardIt, class T >
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value)
{
first = std::find(first, last, value);
if (first != last)
for(ForwardIt i = first; ++i != last; )
if (!(*i == value))
*first++ = std::move(*i);
return first;
}
Conclusion:
If you want to remove elements with some condition, you use vector::iterator erase (iterator first, iterator last); essentially.
First get range start:
auto last = std::remove(vec.begin(), vec.end(), equal_condition_value);
erase by range(always with end())
vec.erase(last, vec.end());
cited:
https://en.cppreference.com/w/cpp/algorithm/remove
Simplest I can come up with:
erase() is something you can do to an element in a container. Given an iterator/index into a container, erase( it ) removes the thing the iterator refers to from the container.
remove() is something you can do to a range, it re-arranges that range but doesn't
erase anything from the range.
remove doesn't "really" remove
anything, because it can't.
In order to "actually" remove the elements from container you need to access container APIs. Where as remove works only with iterators irrespective of what containers those iterators points to. Hence, even if remove wants an "actual remove", it can't.
Remove overwrite "removed" elements by the following elements that were not removed and then it is up to the caller to decide to use the returned new logical end instead of the original end.
In your case remove logically removed 1 from vector a but size remained to 2 itself. Erase actually deleted the elements from vector. [ from vector new end to old end ]
The main idea of remove is it cannot change the number of elements and it just remove elements from a range as per criteria.

Segmentation fault when erasing elements while iterating std::deque

Why does the following code crash? And what should I do when I am iterating via reverse iterator. How do I erase individual elements then?
deque q;
q.push_back(4);
q.push_back(41);
q.push_back(14);
for (auto it = q.begin(); it != q.end();) {
auto current = it;
q.erase(current);
it++;
}
Why does the following code crash ? How do I erase individual elements then ?
std::deque::erase invalidates iterators.
All iterators and references are invalidated, unless the erased elements are at the end or the beginning of the container, in which case only the iterators and references to the erased elements are invalidated.
The past-the-end iterator is also invalidated unless the erased elements are at the beginning of the container and the last element is not erased.
In your code, the iterators to the element to be erased (i.e. it and current) will become invalid after q.erase(current), then it++ will lead to UB.
You could make use of the return value of std::deque::erase
Iterator following the last removed element. If the iterator pos refers to the last element, the end() iterator is returned.
for (auto it = q.begin(); it!=q.end(); )
{
it = q.erase(it);
}
And what should I do if I am iterating via reverse iterator.
Because std::deque::erase doesn't accept reverse_iterator as parameters, you need to use base() to convert it to normal iterator (pay attention to the position conversion). Such as
for (auto it = q.rbegin(); it!=q.rend(); )
{
it = std::make_reverse_iterator(q.erase((++it).base()));
}
As per C++11 23.3.3.4 deque modifiers /4, deque iterators become invalid if you delete certain elements.
An erase operation that erases the last element of a deque invalidates only the past-the-end iterator and all iterators and references to the erased elements.
An erase operation that erases the first element of a deque but not the last element invalidates only the erased elements.
An erase operation that erases neither the first element nor the last element of a deque invalidates the past-the-end iterator and all iterators and references to all the elements of the deque.
In your case, you're usually only ever erasing the first element so it will only invalidate that element. That means the it++ is invalid and you should instead use something like:
it = q.erase(it);
inside the loop, since the erase call itself returns an "adjusted" iterator. This will also work when removing the last element.
However, since your code is totally clearing the list (assuming it's not a cut down version of something which needs to process each element), you can ditch the loop altogether and just use:
q.clear();
As the other answerers have already pointed out, erasing elements from the queue will invalidate the iterators you are using to iterate its elements. Thus it fails.
But I assume that you don't intend to erase all elements in the queue, in which case you probably would have rather used:
q.erase(q.begin(), q.end());
or
q.clear();
Therefore, I'd like to suggest using another technique, that can be used to delete items matching certain criteria from a queue: the erase-remove idiom.
Here, the functions std::remove(...) and std::remove_if(...) are used to move the items to be deleted (matching certain criteria) to the end of the container. The range-based version of q.erase(...) is then used to delete the items.
Here's an example:
#include <deque>
#include <algorithm>
#include <iostream>
// predicate function for removal of elements
bool greater_three(int x) {
return x > 3;
}
int main() {
std::deque<int> q = {1,2,3,4,5};
for (auto i : q) std::cout << i << " "; std::cout << "\n";
// delete all items with value 3
q.erase(std::remove(q.begin(), q.end(), 3), q.end());
for (auto i : q) std::cout << i << " "; std::cout << "\n";
// delete all items with value > 3
q.erase(std::remove_if(q.begin(), q.end(), greater_three), q.end());
for (auto i : q) std::cout << i << " "; std::cout << "\n";
}
The output is:
$ g++ test.cc -std=c++11 && ./a.out
1 2 3 4 5
1 2 4 5
1 2
For reference:
http://en.cppreference.com/w/cpp/container/deque/erase
http://en.cppreference.com/w/cpp/container/deque/clear
http://en.cppreference.com/w/cpp/algorithm/remove
q clearly doesn't support removing elements while iterating through them.

Order of Vector elements for C++

The following piece of c++ code gives
int main()
{
vector <int> myvect(3,0);
vector <int> :: iterator it;
it = myvect.begin();
myvect.insert(it,200);
myvect.insert(it+5,400); //Not sure what 5 makes the difference here
cout << myvect[0] << endl << myvect[1];
}
Output :
200
400
And the same code with minor changes gives
int main()
{
vector <int> myvect(3,0);
vector <int> :: iterator it;
it = myvect.begin();
myvect.insert(it,200);
myvect.insert(it+4,400); //Not sure what 4 makes the difference here
cout << myvect[0] << endl << myvect[1];
}
Output:
400
200
Can someone tell me why adding 4 or 5 to the iterator changes the order of elements?
Thanks
Your program has Undefined Behavior.
You are creating a vector of 3 elements (all initialized to 0), and you are inserting elements at position v.begin() + 5, which is beyond the end of the vector.
Moreover, you are using an iterator (it) after inserting an element before the position it points to. According to Paragraph 23.3.6.5/1 of the C++11 Standard:
[...] If no reallocation happens, all the iterators and references before the insertion point remain valid. [...]
Therefore, iterator it itself is not guaranteed to be valid after the statement myvect.insert(it, 200), and using it in the next instruction (myvect.insert(it + 4, 400)) is again Undefined Behavior.
You cannot expect anything of a program with Undefined Behavior. It may crash, give you bizarre results, or (in the worst case) behave just as you would expect.
The member function vector::insert(const_iterator, const value_type&) requires a valid iterator that refers to the vector but it+4 and it+5 are not valid iterators.
Before the first insertion, it+3 is a valid (non-dereferencable) iterator, pointing just past-the-end of the vector sequence, but it+4 is invalid. After the insertion it might get invalidated, in which case no expression using it is valid, certainly not it+5 because the sequence only has four elements at that point.
The code would be valid if changed like so:
it = myvect.begin();
myvect.insert(it,200);
it = myvect.begin(); // make it valid again
myvect.insert(it+4,400);

Difference between std::vector::front() and begin()

Help on vector says of front()
Returns a reference to the first element in the vector container.
Unlike member vector::begin, which returns an iterator to this same element, this > function returns a direct reference.
Help on vector says of begin()
Returns an iterator referring to the first element in the vector container.
Notice that unlike member vector::front, which returns a reference to the first element, > this function returns a random access iterator.
And this code outputs:
char arr[] = { 'A', 'B', 'C' };
vector<char> vec(arr, arr+sizeof(arr));
cout << "address of vec.front() " << (void*)&vec.front() << endl;
cout << "address of vec.begin() " << (void*)&vec.begin() << endl;
address of vec.front() 00401F90
address of vec.begin() 0030F494
I don't understand what 'direct reference' means? In the case of begin() isn't a random access iterator just a pointer?
Can someone please point out the difference?
According to Stroustrup in The C++ Programming Language, Section 16.3.3; think of front() as the first element and begin() as a pointer to the first element.
In the case of begin() isn't a random access iterator just a pointer?
No, an iterator has some pointer semantics, but it's actually a class.
And even if it was, that should answer the question. It's like asking why the address of a pointer isn't the same as the address of the object it points to.
You'd get the same value if you dereference the iterator, which will give you the first element:
&(*vec.begin())
because
*vec.begin() == vec.front()
For a vector, begin() and end() return random access iterators. They might return a plain pointer; that's okay, because it meets the requirements to be a random access iterator. In particular, you can write *begin() to get a reference to the first object in the sequence (assuming there is one). front() gives you a reference to the first object in the sequence, without going through the intermediate iterator. Like this:
vector<int> v;
v.push_back(3);
int i = *v.begin(); // i == 3
int j = v.front(); // j == 3
Assuming you have at least 1 element in the vector,
vec.front()
is the same as
*vec.begin()
To keep it clear in your mind, always try to remember the following two lines assuming that you have a vector from STL called v:
v.front() = * v.begin()
&&
v.back() = * v.end()
that means if you have a vector:
v = {1,2,3,4};
and you have an iterator called IT, and you want to access the first and the last element
you can either do:
IT = v.begin();
std::cout<<*IT<<std::endl; // output : 1
IT = v.end();
std::cout<<*IT<<std::endl; // output : 4
or you can easily do this:
std::cout<<v.front<<std::endl; // output : 1
std::cout<<v.back<<std::endl; // output : 4
both will print the same output, the difference is just between if you want to use an iterator or not.