I am trying to use iterator in a list as follows, is this the right way to do it?
I have two vectors v1 and v2 and I am using two iterators it1 and it2. Later, I am using a list which is list<vector<int>::iterator> to push_back the iterators it1 and it2. This works, but if any one of the vector is empty, it crashes.
int main() {
vector<int> v1 ={1,2,3};
vector<int> v2 ={4,5,6,7};
vector<int>::iterator it1 = v1.begin();
vector<int>::iterator it2 = v2.begin();
list<vector<int>::iterator> l;
l.push_back(it1);
l.push_back(it2);
for(auto a : l){
vector<int>::iterator it = a;
while(*it){
cout<<*it<<endl;
it++;
}
}
return 0;
}
The crash is coming from trying to dereference it in the line while(*it){. If a vector v is empty, then the iterator v.begin() won't point to valid memory, and dereferencing it will cause a Segmentation Fault.
It will fail as you cannot dereference an iterator until you know its valid to dereference. If the vector is empty begin == end and end is not a valid thing to dereference, as it's one off the end of the container.
You wont be able to iterate your container without knowing the end and you don't capture it, you only capture begin. Simply dereferencing the iterator to test for validity is not correct. It could hold any value.
What about an alternative where you capture a pair of iterators for each vector?
#include <vector>
#include <list>
#include <iostream>
#include <utility>
using namespace std;
int main()
{
const vector<int> v1 ={1,2,3};
const vector<int> v2 ={4,5,6,7};
list<pair<vector<int>::const_iterator,
vector<int>::const_iterator>> L;
L.push_back(make_pair(begin(v1), end(v1)));
L.push_back(make_pair(begin(v2), end(v2)));
for(const auto& a : L){
for(auto it = a.first; it != a.second; ++it)
{
cout << *it << '\n';
}
}
}
Now you have the capability to stop as you know the end iterator too.
list<vector<int>::iterator> l;
l.push_back(it1);
If suppose v1 is empty, then it1 would be v1::end() which is one past the last element of vector and is not part of the vector.So later when you do
while(*it){
you are trying to dereference v::end() which is the reason for the crash.To avoid this you should insert into l only if the iterators are not pointing to end.
if(it1!= v1.end())
l.push_back(it1);
Related
So I need to erase elements from a std::set in a particular order, doing something with the first.
so if I had a set containing {1,2,3,4,5,6} and my I wanted to go until 4, I need to:
doSomething(6);
erase(6);
doSomething(5);
erase(5);
doSomething(4);
erase(4);
I have the following code that does not work:
#include <iostream>
#include <set>
void doSomething(int value) {
std::cout << value << '\n';
}
int main() {
std::set<int> s = {1,2,3,4,5,6};
auto beginIt = s.end();
auto endIt = s.lower_bound(4);
auto rbegin = std::make_reverse_iterator(beginIt);
auto rend = std::make_reverse_iterator(endIt);
for (auto it = rbegin; it != rend;) {
doSomething(*it);
s.erase(std::next(it).base());
}
return 0;
}
I think the issue is that it erasing the end iterator then keeps going util it crashes.
How can I get this to work.
godbolt: https://godbolt.org/z/KvaGWhr4G
The correct way of doing what you want is to getting your end iterator each time.
for (auto it = rbegin; it != std::make_reverse_iterator(s.lower_bound(4));) {
doSomething(*it);
s.erase(std::next(it).base());
}
Now let's see why you initial code didn't work.
In set, the iterators are not invalidated after erasing an element, EXCEPT for the iterator that was pointing to the erased element.
Now let's see what happened in the last iteration when you remove 4.
When dereferencing the rend, we will see that it points to 3. However, the base of rend points to 4. And after removal of 4, the base of rend has been invalidated. So your program had Undefined behavior.
To understand why getting end iterator at every iteration works, we have to understand that during the program the base of it is always s.end(). And at the last step, when we call s.lower_bound(4), we get s.end(). Hence, the condition for exiting the loop is satisfied.
I am learning to use stl vector and It is odd that this program cannot work. What is wrong with it? How should I do if I want to implement the same function with vector?
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> vec;
vector<int>::iterator it;
vector<int>::iterator temp;
it = vec.begin();
vec.insert(it, -1);
it++;
vec.insert(it, 2);
for(temp = vec.begin();temp!=vec.end();temp++)
cout<<*temp<<' ';
return 0;
}
vec.insert(it, -1); invalidates it.
You should rather use it = vec.insert(it, -1); which will keep it valid.
You can see the documentation:
https://en.cppreference.com/w/cpp/container/vector
section called "Iterator invalidation" or look at this great question and answer: Iterator invalidation rules
On executing the code
vector<int> vec;
You created an object named vec, it has no elements and vec.size() will be zero.
So what vec.begin() returns is the same as what vec.end() returns.
By doing vec.insert(it, -1); you are inserting a value out of vec's range.
That is undefined behavior.
No, vec.insert(it, -1) works well, but vec.insert(it, -1) causes the vector to reallocate memory for its first element.
That invalids it.
Try vec.emplace_back(-1) or vec.resize(2) instead. They extend vec's size and capacity.
I have to read only the first two elements from an std::map.
Here's my code:
#include<iostream>
#include<map>
#include<iterator>
using namespace std;
int main() {
map<int,int> a;
map<int,int>:: iterator itr;
itr = a.begin();
cout<<itr->first<<" "<<itr->second<<endl;
next(itr);
cout<<itr->first<<" "<<itr->second<<endl;
return 0;
}
I'm getting this error:
next was not declared in the scope
what am I missing here and if there is a better way to do it?
For using std::next, you need to have at least C++11 compliant compiler.
std::next returns a new incremented iterator. So, you need to use its return value to get the incremented iterator i.e.:
itr = next( itr );
Right now, itr is pointing to the same element because the return value is not used.
If you meant to increment the itr without a new iterator then std::advance is a better candidate here i.e.:
std::advance( itr, 1 );
If you're using a pre-C++11 compiler then you can use increment operator like this:
itr++;
Or,
++itr;
Here's a live demo.
In addition, the map is uninitialized/empty and you're trying to access its elements that are not there and this would result in Undefined Behavior.
Relevant read:
Why is "using namespace std;" considered bad practice?
C++: "std::endl" vs "\n"
you should use itr++ to move itr forward to point next set of pairs. and you can access them by map->first; and map->second;
I have a std::vector and an iterator that points to an element in the vector. My question is how can I delete an element from the vector and keep the iterator?
I've tried using a second iterator to find the specific element that I want to delete and after erasing it with the erase function, the first iterator becomes invalid.
I have a std::vector and an iterator that points to an element in the vector. My question is how can I delete an element from the vector and keep the iterator?
Please note that when an element is deleted, no iterator can point to it as it ceases to exist. So, to reference it's location normal practice is just use the returned iterator from the erase() method. This allows use of the insert() method which will put a value in the position of the previously erased object.
With one iterator, just use this:
auto loc_after = v.erase(iter); // since the element has been erased loc_after points to the position the erased element had
I've tried using a second iterator to find the specific element that I want to delete and after erasing it with the erase function, the first iterator becomes invalid.
In the case of two iterators the elements can be easily erased by erasing the physically last iterator first since the earlier iterator is not invalidated. This is encapsulated in a function. The returned iterator points to one past the position of the "first" iterator regardless of order between the first and second iterator.
#include <vector>
#include <iostream>
// This returns an iterator positioned after where first_iter was before being erased
// this allows the insert(pos, val) method to insert a value in the the location just prior to pos
std::vector<int>::iterator second_iterator_loc(std::vector<int>& v, std::vector<int>::iterator first_iter, std::vector<int>::iterator second_iter)
{
std::vector<int>::iterator iter;
if (first_iter < second_iter)
{
v.erase(second_iter);
iter = v.erase(first_iter);
}
else if (second_iter < first_iter)
{
auto dist = first_iter - second_iter;
v.erase(first_iter);
iter = v.erase(second_iter) + dist - 1;
}
else
{
;// handler in case both iterators point to the same object
}
return iter;
}
int main()
{
std::vector<int> v{ 1,2,3,4,5 };
std::vector<int> v2 = v;
std::vector<int>::iterator iter1 = v.begin() + 1; // points to 2 in v
std::vector<int>::iterator iter2 = v.begin() + 3; // points to 4 in v
std::vector<int>::iterator iter;
iter = second_iterator_loc(v, iter1, iter2);
v.insert(iter, 9); // inserts a 9 in the previous location of the "first" iterator (where "2" was)
for (auto x : v)
std::cout << x << '\n'; // prints: 1 9 4 5
v = v2;
iter1 = v.begin() + 3; // reverse iterator positions
iter2 = v.begin() + 1;
iter = second_iterator_loc(v, iter1, iter2);
v.insert(iter, 9); // inserts a 9 in the previous location of the "first" iterator (where "4" was)
for (auto x : v)
std::cout << x << '\n'; // prints: 1 3 9 5
}
My question is how can I delete an element from the vector and keep the iterator?
You can't using std::vector::iterator. The iterator will be invalidated by erasing the element.
But you could achieve this by writing your own iterator class that stores a pointer to the vector and an index.
std::vector::erase will invalidate all iterators at or after the erased element:
Invalidates iterators and references at or after the point of the
erase, including the end() iterator.
However, erase will return an iterator pointing to the element following the last removed element. Maybe that is enough to satisfy your use case?
I have problem in the following simple code:
void foo (vector<int>:: iterator it, vector<int> n)
{
vector<int>:: iterator it2 = it +1;
while (it2!=n.end())
{
cout<<*it2<<endl;
it2++;
}
}
main()
{
vector<int> m{1,2,3,4};
vector<int>:: iterator it = m.begin();
foo (it, m);
}
I expected to have 2, 3 and 4 in the Terminal, but I got some stupid results in output. Is it basically possible to use iterators as functions' input? What is wrong in this piece of code? and How can I make it correct?
You pass vector<int> n as a copy. Thus your it2 points to a different vector (the one that was created in main). Your check it2!=n.end() is invalid since it2 is an iterator to another vector.
Passing n by reference is one solution. Other would be passing the end iterator instead of vector.
To pass your vector as a const reference:
void foo (vector<int>:: iterator it, const vector<int>& n)
To pass an end iterator:
void foo (vector<int>::iterator it, vector<int>::iterator end)
{
...
while ( it2 != end )
...
}
You have two problems: one is that you're passing a copy of your vector argument, as Satus and yeputons already pointed out.
The second problem is that the first line of foo is already illegal if the argument is empty. That is, even the trivial fix
void foo (vector<int>:: iterator it, vector<int> &n)
{
vector<int>:: iterator it2 = it +1;
is wrong if it == n.end().
The correct design is the one used for all the library algorithms, and for the same reason: that it can correctly express empty ranges.
void foo (vector<int>::iterator begin, vector<int>::iterator end)
{
if (begin == end) return;
for (auto i = begin; i != end; ++i)
{
cout<<*i<<endl;
}
}
Your weird skipping-the-first-element design makes it a bit ugly still, a nicer approach is to have some utility help skip the first element, and then use copy:
template <typename Iterator>
Iterator try_advance(Iterator i, int count, Iterator end)
{
for (; count-- > 0 && i != end; ++i)
;
return i;
}
void foo (vector<int>::iterator begin, vector<int>::iterator end)
{
// skip first element of a non-empty range
// leave an empty range un-damaged
begin = try_advance(begin, 1, end);
std::copy(begin, end, std::ostream_iterator<int>(std::cout, '\n'));
}
Yes, it's possible. But mind that iterators are tied to their container.
Second parameter of your function is copy-constructed from argument, i.e. vector<int> n is a copy of vector<int> m defined in main. So, your function tries to compare iterator with another iterator (.end()) from a different container. You'd better pass both begin/end iteratos instead of container.