c++11 insert a vector into a particular position - c++

I am using c++11, I like to insert a vector into a particular position of another vector, here is a simplified code:
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
vector<int> v1 = {1, 2, 3};
vector<int> v2 = {0, 3, 9};
vector<int>::iterator itr = find(v2.begin(), v2.end(), 3);
itr = v2.erase(itr);
// like to insert "3", "2" and "1" to the same position which ends up "1", "2" and "3"
for (auto ri = v1.rbegin(); ri != v1.rend(); ++ri) {
v2.insert(itr, *ri);
}
for (const auto &i : v2) {
cout << i << " ";
}
cout << endl;
return 0;
}
The above code crashes.
Yes, I know some other STL API like transform() or copy() might be the one to use, but just wonder what is wrong of the above code?

Your current code crashes because itr is invalidated when insert() reallocates v2 after it exceeds its max capacity.
Change the following:
v2.insert(itr, *ri);
to
itr = v2.insert(itr, *ri);

Use this API
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
So in your case, replace the second loop with:
insert(itr,v2.begin(),v2.end())

what is wrong of the above code?
The iterator itr became invalid after insert, so program crashed at the 2nd execution of the for loop. You can use the return value of insert (iterator pointing to the inserted value) to get a valid iterator for insert position.
Change
v2.insert(itr, *ri);
to
itr = v2.insert(itr, *ri);
insert causes reallocation if the new size() is greater than the old capacity(). If the new size() is greater than capacity(), all iterators and references are invalidated. Otherwise, only the iterators and references before the insertion point remain valid. The past-the-end iterator is also invalidated.
Reference: http://en.cppreference.com/w/cpp/container/vector/insert
PS: Please see #Kam's answer, it's a better solution than hand-written loops in general cases.

Related

How do I remove element pointed to by iterator in a C++ list?

How do I remove element pointed to by iterator in a C++ list? why does not this work?
int main()
{
list<int> l;
l.push_back(5);
l.push_back(6);
for(auto &it: l)
{
l.erase(*it);
}
return 0;
}
Why
for(auto &it: l){
l.erase(*it);
}
fails to work:
it is not an iterator. In a range-based for loop, the variable declared before the colon, the range_declaration, will receive an item in the container, an int in this case. Since it will receive an int, auto will infer a type of int resulting in
for(int &it: l){
l.erase(*it);
}
and std::list::erase requires an iterator. I'm assuming the * is simply the result of a bit of shotgun debugging to see if dereferencing what was believed to be an iterator helped (it wouldn't).
Side note: You cannot remove an item from a container while iterating the container with a range-based for loop. The magic in the background that implements the for loop looks something like
{
auto cur = container.begin() ;
auto end = container.end();
for ( ; cur != end; ++cur)
{
auto val = *cur;
do_stuff
}
}
If in do_stuff you remove cur from the container, ++cur is invalid. Since cur's not in the container anymore, you can't use it to advance to the next item in the container. std::list is very permissive in its iterator invalidation rules. Many containers will fail when the cached end iterator is invalidated.
How to fix:
The given code appears to be trying to empty all the items in the list. std::list::clear does that for you with a single function call.
If you want to release a particular element or select elements by value, you should use std::list::remove or std::list::remove_if in conjunction with std::list::erase
eg:
l.erase(l.remove(5), l.end()); // remove all elements containing the number 5
if you want to remove the first item, std::list::pop_front. To remove the last item, std::list::pop_back. If you want to remove any other element by position, you must have a valid iterator for that position (If you do not already have one, see std::advance) and then call erase. Note that if you're having to iterate a lot to find items to remove, std::list may not be the right container for this job because list iteration is expensive and quickly eliminates the benefits of cheap insert and removal.
int main()
{
list<int> l;
list<int>::iterator it;
l.push_back(5);
l.push_back(6);
l.push_back(7);
it=l.begin();// points to the first element
l.erase(it);//pass the iterator to the erase method
for(auto i=l.begin();i!=l.end();i++){
cout<<*i<<" ";
}
return 0;
}
lets say you want to erase the first element. Then simply pass the iterator of the list to erase method.
If you wanted do it in loop cpprefenrence has nice example
when an element removed using erase method, it returns the next iterator to removed element, if last element end will return;
std::list<int> l{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
from above list, let's assume you wanted to remove 4 and 5;
below is the way;
std::list<int>::iterator first = l.begin();
std::advance( first, 4 );
auto it = l.erase( first ); // removes 4 and returns iterator to element 5
l.erase( it ) // removes 5;
as others suggested:
for(auto &it: l){ // range based loop, iterating through elements ex: 4, 5, 6
//l.erase(*it);
std::cout << it; // prints 4, 5, 6
}
you need below for loop to increment iterator
for( auto it = l.begin(); it != l.end(); it++)
{
// do something hear
}
If you use an iterator-based loop, you can use the return value of erase to update the iterator:
std::list<int> l = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (auto it = l.begin();
it != l.end();
++it)
{
if (*it % 3 == 0)
{
it = l.erase(it);
}
}
for (auto i : l)
{
std::cout << i << std::endl;
}

Removing duplicate elements in using the function "remove"

I tried to remove duplicate elements from a vector by a function vectorremove, using the function remove from the library of algorithms, but it does not work:
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
void vectorremove(vector<string> v)
{
for (vector<string>::iterator it = v.begin(); it != v.end(); ++it)
{
vector<string>::iterator end = remove(it + 1, v.end(), *it);
v.erase(end, v.end());
}
}
int main()
{
vector<string> vect;
string x;
while (cin >> x)
{
vect.push_back(x);
}
vectorremove(vect);
for (vector<string>::iterator it = vect.begin(); it != vect.end(); ++it)
{
cout << *it << endl;
}
return 0;
}
I wrote this code to test if the function vectorremove works, unfortunately it seems that vectorremove has no impact on the vector. Have I made any mistake in the use of remove in the definition of vectorremove?
The first problem in your code is that you are passing the vector by value and not by reference to vectorremove. You need to change that to
void vectorremove(vector<string>& v);
Then inside your vectorremove function you have another problems. vector::erase can invalidate all iterators, so you should onle remove inside the loop and do the erase after the loop.
void vectorremove(vector<string>& v)
{
vector<string>::iterator end{ v.end() };
for (vector<string>::iterator it = v.begin(); it != end; ++it)
{
end = remove(it + 1, end, *it);
}
v.erase(end, v.end());
}
First you are passing std::vector by value, not by reference. Therefore, any changes you make in vectorremove function won't be visible in main.
Furthermore, std::vector::erase might invalidate iterators so you must not use it inside the loop.
Your code could look like:
void vectorremove(std::vector<std::string>& v) {
auto end{ v.end() };
for (auto it = v.begin(); it != end; ++it)
{
end = std::remove(it + 1, end, *it);
}
v.erase(end, v.end());
}
Note the usage of auto instead of std::vector<std::string>::iterator.
However, STL provides handy functions to achieve what you want. One of them is std::unique which
Eliminates all but the first element from every consecutive group of
equivalent elements from the range [first, last) and returns a
past-the-end iterator for the new logical end of the range.
In order to remove duplicates from the std::vector, you can do something like:
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v{ 1, 2, 3, 1, 2, 3, 3, 4, 5, 4, 5, 6, 7 };
std::sort(v.begin(), v.end()); // 1 1 2 2 3 3 3 4 4 5 5 6 7
auto last = std::unique(v.begin(), v.end());
v.erase(last, v.end());
for (auto const i : v) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
Remember that std::unique works as expected only on sorted std::vectors.
You only modify the copy of vector , you have to pass by reference to modify the actual
vector, why you don't use auto instead of std::vector::iterator. and you have to
know that erase invalidates all iterators pointing to the erased element and
beyond the erased element, keep the iterator valid by using erase's return value, also use std::getline inside the loop to store the value from std::cin to include the new line.
Alternatively your can use std::unique removes the duplicate value and works as expected after sorting the elements. and std::unique returns a past the end iterator for the new logical end of the range:-
#include <vector>
#include <algorithm>
#include <string>
std::vector<std::string> removeDuplicate(std::vector<std::string> & v){
std::vector<std::string> vec;
std::sort(std::begin(v), std::end(v));
auto pos = std::unique(std::begin(v), std::end(v));
vec.assign(std::begin(v), pos);
return vec;
}
int main(){
std::vector<std::string> vect{"John", "John", "Paul", "John", "Lucy", "Bob", "Bob"};
auto pureVector = removeDuplicate(vect);
for(auto const & v : pureVector){
std::cout << v << '\n';
}
}

Converting const auto & to iterator

A number of posts I've read lately claim for(const auto &it : vec) is the same as using the longer iterator syntax for(std::vector<Type*>::const_iterator it = vec.begin(); it != vec.end(); it++). But, I came upon this post that says they're not the same.
Currently, I'm trying to erase an element in a for loop, after it is used, and wondering if there is any way to convert const auto &it : nodes to std::vector<txml::XMLElement*>::iterator?
Code in question:
std::vector<txml2::XMLElement *> nodes;
//...
for (const auto &it : nodes)
{
//...
nodes.erase(it);
}
I pretty sure I could just rewrite std::vector<txml2::XMLElement*> as a const pointer, but would prefer not to since this code is just for debugging in the moment.
You should not be attempting to convert the range declaration in your range based for loop to an iterator and then deleting it whilst iterating. Even adjusting iterators while iterating is dangerous, and you should instead rely on algorithms.
You should use the Erase-remove idom.
You can use it with remove_if.
It would look something like:
nodes.erase( std::remove_if(nodes.begin(), nodes.end(), [](auto it){
//decide if the element should be deleted
return true || false;
}), nodes.end() );
Currently in the technical specifications, is erase_if.
This is a cleaner version of the same behaviour shown above:
std::erase_if(nodes,[](auto it){
//decide if the element should be deleted
return true || false;
});
You don't get an iterator but a reference to the element. Unless you want to do a std::find with it, it's pretty hard to get an iterator out of it.
Vectors are nice, so you could increase a counter per element and do nodes.begin() + counter to get the iterator, but it'd sort of defeat the point.
Also erasing the iterator in the for loop will result in you iterating after the end of the vector, you can test this code:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v = {0,1,2,3,4,5,6};
for (int x : v) {
cout << x << endl;
if (x == 2) {
v.erase(v.begin() + 2);
}
}
return 0;
}
If you want to use iterators, just do a loop with them, if in addition you want to erase one mid-loop you have to follow this answer:
for (auto it = res.begin() ; it != res.end(); ) {
const auto &value = *it;
if (condition) {
it = res.erase(it);
} else {
++it;
}
}
Note that you don't need to specify the whole type of the iterator, auto works just as well.

Convert rend iterator to end iteterator

This is not a duplicate of how to convert a reverse_iterator to an iterator because I want the result to be different to the normal conversion.
Given only a reverse_iterator that is returned from rend, is it possible to convert this to the corresponding iterator that would be returned from end?
For example
std::vector<int> myvec = {...};
auto rit = myvec.rend();
auto it = MAGIC(rit);
it == myvec.end(); // true
If it is not possible to do this given only the reverse_iterator, what is the minimum information needed to do this? (So I can consider workarounds).
Short answer: No.
An iterator refers to one single point in a container, wihtout actual knowledge of the container itself. The iterators returned by end() and rend() point to different ends of a container, i.e. there might be some, many or no elements between the points they refer to, regardless of the reverse nature of one of the iterators. So, without knowing about the container itself or at least its size, there is no possibility to get from one end of the container to the other, and since iterators don't have that knowledge, there is no possibility to get from rend() to end(), from end() to begin() etc. without additional information.
The minimum needed information is the size of the "gap" between the two points. With that and the normal conversion between reverse and non-reverse iterators it is an easy task:
auto rend = v.rend();
auto begin = rend.base();
assert(begin == v.begin());
auto end = begin + v.size(); //the size is the key!
assert(end == v.end());
But, since you can not obtain the size from the reverse_iterator but only from the container itself, you can easily ask it for end() in the first place.
Expression
myvec.rend().base()
is equivalent to
myvec.begin()
Here is a demonstrative example
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
std::vector<int>::iterator it = v.rend().base();
std::cout << *it << std::endl;
return 0;
}
The output is
1
Another demonstrative program that shows the relation between std::vector<int>::iterator and std::vector<int>::reverse_iterator (instead of templetae argument int you may use any type T>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
if ( v.begin() == v.rend().base() )
{
std::cout << "v.begin() == v.rend().base()" << std::endl;
}
if ( v.end() == v.rbegin().base() )
{
std::cout << "v.end() == v.rbegin().base()" << std::endl;
}
return 0;
}
The output is
v.begin() == v.rend().base()
v.end() == v.rbegin().base()

Iterator invalidation - does end() count as an iterator or not?

I ran into the following problem using std::multimap::equal_range() and insert().
According to both cplusplus.com and cppreference.com, std::multimap::insert does not invalidate any iterators, and yet the following code causes an infinite loop:
#include <iostream>
#include <map>
#include <string>
int main(int argc, char* argv[])
{
std::multimap<std::string,int> testMap;
testMap.insert(std::pair<std::string,int>("a", 1));
testMap.insert(std::pair<std::string,int>("a", 2));
testMap.insert(std::pair<std::string,int>("a", 3));
auto range = testMap.equal_range(std::string("a"));
for (auto it = range.first; it != range.second; ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
// this loop becomes infinite
}
// never gets here
for (auto it = testMap.begin(); it != testMap.end(); ++it)
{
std::cout << it->first << " - " << it->second << std::endl;
}
return 0;
}
The intent is to take all existing items in the multimap with a particular key ("a" in this case) and duplicate them under a second key ("b"). In practice, what happens is that the first loop never exits, because it never ends up matching range.second. After the third element in the map is processed, ++it leaves the iterator pointing at the first of the newly inserted items.
I've tried this with VS2012, Clang, and GCC and the same thing seems to happen in all compilers, so I assume it's "correct". Am I reading too much into the statement "No iterators or references are invalidated."? Does end() not count as an iterator in this case?
multimap::equal_range returns a pair whose second element in this case is an iterator to the past-the-end element ("which is the past-the-end value for the container" [container.requirements.general]/6).
I'll rewrite the code a bit to point something out:
auto iBeg = testMap.begin();
auto iEnd = testMap.end();
for(auto i = iBeg; i != iEnd; ++i)
{
testMap.insert( std::make_pair("b", i->second) );
}
Here, iEnd contains a past-the-end iterator. The call to multimap::insert doesn't invalidate this iterator; it stays a valid past-the-end iterator. Therefore the loop is equivalent to:
for(auto i = iBeg; i != testMap.end(); ++i)
Which is of course an infinite loop if you keep adding elements.
The end-iterator range.second is not invalidated.
The reason that the loop is infinite, is that each repetition of the loop body:
inserts a new element at the end of the map, thus increasing the distance between it and the end by one (so, after this insert, range no longer represents the equal_range for the key "a" because you have inserted a new key within the range it does represent, from the first "a" to the end of the container).
increments it, reducing the distance between it and the end by one.
Hence, it never reaches the end.
Here's how I might write the loop you want:
for (auto it = testMap.lower_bound("a"); it != testMap.end() && it->first == "a"; ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
}
A solution to make it work as expected (feel free to improve, it's a community wiki)
auto range = testMap.equal_range(std::string("a"));
if(range.first != range.second)
{
--range.second;
for (auto it = range.first; it != std::next(range.second); ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
}
}