According to the documentation, hashed index iterators remains valid when new elements are inserted into a multi_index. However when I attempted the following approach
auto& myIndex = myMultiIndex.get<0>();
auto range = myIndex.equal_range(x);
for (auto iter = range.first; iter != range.second; ++iter) {
myMultiIndex.emplace(someArgsRelatedToIter);
}
the range.first/range.second seem to become invalid: even though std::distance(range.first, range.second) == 1, the for loop actually gets executed twice. Am I somehow not using it correctly? Thanks!
When c++ libraries specify that iterators aren't invalidated by an operation it means that the iterators still point to the same element. For example in the following code:
std::list< int > l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
auto first = l.begin();
auto last = std::find(l.begin(), l.end(), 3);
std::cout << *first << std::endl;
std::cout << *last << std::endl;
1, 3 and 2 are printed. If we now insert some elements:
l.insert(last, 4);
l.insert(last, 5);
std::cout << *first << "\n";
std::cout << *last << "\n";
std::cout << std::distance(first, last) << "\n";
first and last are still valid and point to the same elements but the distance is now 4 so they point to a different range.
Related
In C++ I can use reverse_iterator to loop elements of container, say, list, backwards.
How do I iterate over a certain number of elements? Not reaching the beginning? This code works, but I feel there is a better way.
std::list<int> mylist{1,2,3,4,5};
int cnt = 3;
for (auto rit = mylist.rbegin(); rit != mylist.rend(); ++rit) {
if (cnt == 0) break;
std::cout << *rit << " ";
--cnt;
}
Expected output {5, 4, 3}.
You can adjust the loop as follows:
for (auto rit = mylist.rbegin(); rit != std::next(mylist.rbegin(), 3); ++rit)
{
std::cout << *rit << " ";
}
but note that for this to work reliably, you need to check that the list is at least of size 3 or adjust the parameter to std::next as in const auto n = std::min<std::size_t>(3, mylist.size());.
With C++20, you should be able to go with (obviously not tested)
#include <ranges>
for (int n : mylist | std::ranges::reverse_view | std::ranges::take_view(3))
std::cout << n << "\n";
This renders the size testing superfluous, as take_view is bound by the range size (it performs this check internally).
Not reaching the beginning?
If you can guarantee that, then
std::copy_n(mylist.rbegin(), 3, std::ostream_iterator<int>(std::cout, " "));
Or to be safer
std::copy_n(mylist.rbegin(), std::min(mylist.size(), std::list<int>::size_type(3)), std::ostream_iterator<int>(std::cout, " "));
LIVE
I would not put incrementing/decrementing and checking the condition in the loop body, but rather put it where you most expect it:
std::list<int> mylist{1,2,3,4,5};
int cnt = 3;
for (auto rit = mylist.rbegin(); rit != mylist.rend() && cnt > 0; ++rit,--cnt) {
std::cout << *rit << " ";
}
Now you could for example add a continue anywhere in the loop body without making the loop go havoc.
PS: actually I like the other answers better, but I'll leave this one as it is minimal changes and imho incrementing / checking a loop counter inside the loop body is something to be avoided if possible (and it is possible here).
Take the following example taken from the cplusplus.com reference page and altered to return false:
// find_if example
#include <iostream> // std::cout
#include <algorithm> // std::find_if
#include <vector> // std::vector
bool IsOdd (int i) {
return ((i%2)==1);
}
int main ()
{
std::vector<int> myvector;
myvector.push_back(10);
myvector.push_back(20);
myvector.push_back(40);
myvector.push_back(50);
std::vector<int>::iterator it = std::find_if (myvector.begin(), myvector.end(), IsOdd);
std::cout << "The first odd value is " << *it << '\n';
return 0;
}
Since no value in myvector is odd it will return InputIterator last, which is undefined:
The first odd value is -1727673935
What is the proper way to handle this output?
How can I know std::find_if() returned false if the output is unpredictable and comparing to the entire vector to confirm the resulting value doesn't exist defeats the purpose of using std::find_if() to begin with?
Do you mean
std::vector<int>::iterator it = std::find_if (myvector.begin(), myvector.end(), IsOdd);
if ( it != myvector.end() )
{
std::cout << "The first odd value is " << *it << '\n';
}
else
{
// std::cout << "there is no odd value in the vector\n";
}
The idiomatic way to do this is to check whether the iterator equals the end sentinel.
auto it = std::find_if (myvector.begin(), myvector.end(), IsOdd);
if (it == myvector.end()) {
std::cout << "No odd values found" << std::endl;
} else {
std::cout << "The first odd value is " << *it << std::endl;
}
In C++17 (the most recent standard), you can declare the iterator right in the if statement:
if (auto it = std::find_if(myvector.begin(), myvector.end(), IsOdd); it != myvector.end()) {
std::cout << "The first odd value is " << *it << std::endl;
} else {
std::cout << "No odd values found" << std::endl;
}
You need to check if the returned iterator is the end iterator you passed to std::find_if (the second argument). These semantics are quite common for algorithms in the standard library, so you should get used to this.
const auto firstOdd = std::find_if (myvector.cbegin(), myvector.cend(), IsOdd);
if (firstOdd != myvector.cend())
std::cout << "The first odd value is " << *it << '\n';
else
std::cout << "No odd values found\n";
Note also that you can use the cbegin()/cend() member functions, as you're not mutating the container.
std::find_if returns(reference cppreference.com)
Iterator to the first element satisfying the condition or last if no
such element is found.
That means, dereference the iterator only when its not equal to container.end() iterator.
if (const auto iter = std::find_if(myvector.cbegin(), myvector.cend(), IsOdd); // need C++17 compiler support
iter != myvector.cend())
{
std::cout << *iter << "\n";
}
else
{
// code
}
PS: In modern C++, lambdas expressions should be your good friends, and use it when it is appropriate.
See more here: Why can lambdas be better optimized by the compiler than plain functions?
That means your IsOdd could have been
constexpr auto isOdd = [](const int i) /* noexcept */ { return i & 1; };
I have looked for this and found something here: Variable not printing on iteration but I'm not sure if that necessarily applies.
What I have going on is my program correctly prints all values when I call it like this:
for (int i = 0; i < SampleVec.Matchups().size(); ++i){
std::cout << SampleVec.Matchups()[i] << std::endl;
}
or when I call it like this:
std::vector<int> temp;
temp = SampleVec.Matchups();
for (std::vector<int>::const_iterator iter = temp.begin(); iter != temp.end(); iter++){
std::cout << *iter << std::endl;
}
but when I write it like this
for (std::vector<int>::const_iterator iter = SampleVec.Matchups().begin(); iter != SampleVec.Matchups().end(); iter++){
std::cout << *iter << std::endl;
}
the first two values show up as a 0 and the rest print correctly. In the link I posted they talk about stripping newlines from the input, but I don't know if that applies here or even how to do that. I can post full code if needed to run and see the functionality in action.
for (std::vector<int>::const_iterator iter = SampleVec.Matchups().begin(); iter != SampleVec.Matchups().end(); iter++){
std::cout << *iter << std::endl;
}
begin() returns the iterator of the beginning of a temporary std::vector returned by Matchups(). At the moment of using iter it's a dangling iterator because the temporary has been destroyed and thus you have Undefined Behaviour.
You have to store the result before trying to access it through an iterator like you do in example 2.
The problem is that I need to find all elements with the same value in a vector, do something with them, then erase all of them out of the vector. Keep doing that until the vector is empty.
vector<unsigned> L;
vector<unsigned>::iterator it, it2, it3;
vector<unsigned> vec;
unsigned Z;
// populate the vector (1, 2, 3, 4, 2, 4)
for(unsigned i = 1; i <= 4; ++i)
L.push_back(i);
for(unsigned i = 2; i <= 4; i = i + 2)
L.push_back(i);
it = L.begin();
while(it != L.end() -1){
cout<< "*it = " << *it << endl;
Z=0;
vec.clear();
it2 = it + 1;
cout<< "*it2 = " << *it2 << endl;
while(it2 != L.end()){
cout << "Loop *it2 = " << *it2 <<endl;
if(*it == *it2){
vec.push_back(*it);
L.erase(it2); // iterator automatically points to the next element
cout<< "after erase(it2), *it2 = " << *it2 << endl;
continue;
}
++it2;
}
// do something (here I calculate the average)
for(it3 = vec.begin(); it3 != vec.end(); ++it3)
Z = Z+ *it3;
Z= Z/vec.size();
cout<< "Z = " << Z << endl << endl;
L.erase(it); // iterator automatically points to the next element
}
The output is:
*it = 1
*it2 = 2
Loop *it2 = 2
Loop *it2 = 3
Loop *it2 = 4
Loop *it2 = 2
Loop *it2 = 4
Then it stops working
If I populate the vector with this code
// populate the vector (1, 2, 3, 4, 1, 3)
for(unsigned i = 1; i <= 4; ++i)
L.push_back(i);
for(unsigned i = 1; i <= 4; i = i + 2)
L.push_back(i);
Then the output is
*it = 1
*it2 = 2
Loop *it2 = 2
Loop *it2 = 3
Loop *it2 = 4
Loop *it2 = 1
after erase(it2), *it2 = 3
Loop *it2 = 3
Z = 1
*it = 2
*it2 = 3
Loop *it2 = 3
Loop *it2 = 4
Loop *it2 = 3
It stops working here
I know there is something wrong in the second while loop but I can't figure out what it is. Any help would be appreciated.
If the goal is to
Process duplicates and then
Erase them
there is a much easier solution to this, and that is to use std::stable_partition, along with a std::set:
#include <algorithm>
#include <vector>
#include <iterator>
#include <set>
#include <iostream>
#include <numeric>
using namespace std;
int main()
{
std::vector<int> L = { 1, 2, 4, 3, 2, 4 };
std::set<int> tempSet;
//...
// partition the elements, unique items on left, duplicates on right
auto divider = stable_partition(L.begin(), L.end(), [&](int n)
{
// return true if item is not in the set, false otherwise
return tempSet.insert(n).second;
});
// do something with the duplicates, for example, print them
cout << "Here are the dups:\n";
copy(divider, L.end(), ostream_iterator<int>(cout, " "));
// get the average
// get number of duplicates
size_t numDups = std::distance(divider, L.end());
double avg = 0.0;
// compute average using std::accumulate
if ( numDups > 0 )
avg = std::accumulate(divider, L.end(), 0.0) / numDups;
cout << "\nThe average of the duplicates is: " << avg << "\n";
// erase the duplicates
L.erase(divider, L.end());
// print the updated vector now
cout << "\n\nHere is the resulting vector:\n";
copy(L.begin(), L.end(), ostream_iterator<int>(cout, " "));
}
Live Example, C++ 14
Here is the same code, but using C++ 03 (for those without C++11 / 14):
Live Example, C++ 03
Note there are no loops in the code above. Everything is done by an algorithm function. Partitioning, average computation, erasing, etc., are all performed with no loops.
Basically we test each item to see if the item exists in the set. If it does, then it will go to the right of the partition, if not, then the item goes to the left of the partition.
The return value divider is an iterator to the element that is the "dividing line" in the partition. Once the items are partitioned, then we can process them easily by using divider to tell us where the "good" items are and where the "about to be erased" items are.
BTW, this worked the first time I compiled it successfully -- the one major reason for this "luck" in getting it to work quickly is that the algorithm functions just plain work when given the correct parameters (and if the predicate function is written correctly). Erasing items, moving items, etc. in a container, especially a sequence container such as vector, is almost always covered by usage of 1, 2, or 3 algorithm functions.
This is my solution, which was inspired by PaulMcKenzie's code (please don't mind my re-using some of your lines). Just like him I didn't use any handwritten loops, but made use of STL's algorithms, which one should always consider first.
In my solution I only use another container of exactly the same type and only two simple algorithms: std::remove_if and std::find. This way the code makes the intention a little bit clearer: find duplicates and remove them. Also note the use of std::move that should come in handy when having a container of something more complex than int. In that case one may have to consider using std::find_if of course.
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
std::vector<int> L = { 1, 2, 4, 3, 2, 4 };
using cont = decltype(L);
using vt = cont::value_type;
// find duplicates and move them to separate container
cont D;
D.reserve(L.size());
D.swap(L);
D.erase(std::remove_if(D.begin(), D.end(), [&L] (vt& value)
{
if (L.cend() == std::find(L.cbegin(), L.cend(), value))
{
L.emplace_back(std::move(value));
return true;
}
return false;
}), D.end());
// do something with the duplicates, for example, print them
std::cout << "Here are the dups:\n";
std::copy(D.cbegin(), D.cend(), std::ostream_iterator<vt>(std::cout, " "));
// print the vector now
std::cout << "\n\nHere is the resulting vector:\n";
std::copy(L.begin(), L.end(), std::ostream_iterator<vt>(std::cout, " "));
return 0;
}
See it in action here.
The problem is with your erase call. erase does not automatically update the iterator; it returns the iterator to the next element. To fix it, you need to use
it2 = L.erase(it2);
and later
it = L.erase(it);
vector<unsigned> L;
vector<unsigned>::iterator it, it2, it3;
vector<unsigned> vec;
unsigned Z;
// populate the vector (1, 2, 3, 4, 2, 4)
for(unsigned i = 1; i <= 4; ++i)
L.push_back(i);
for(unsigned i = 2; i <= 4; i = i + 2)
L.push_back(i);
std::set<unsigned int> myset;
for(it = L.begin(); it != L.end(); ++it){
myset.insert(*it);
}
for(std::set<unsigned int>::iterator iter = myset.begin();
iter != myset.end(); ++iter){
std::cout << "element = " << *iter << std::endl;
Z += *iter;
}
std::cout << "sum = " << Z << std::endl;
Z = Z/myset.size();
std::cout<< "Average value = " << Z << std::endl;
L.clear();
I finally found out the solution for my own question. The question is
We have a vector L that contains many duplicated elements.
We need to find all the dup elements
Do something with the dup elements
My method is to use 2 iterators on L, say it and it2. it is kept at the beginning of L, while it2 will go through the vector. If *it and *it2 have the same value, then we will put *it2 into a temporary vector vec. Then, we L.erase(it2). it2 will keep moving till the end of L, collect all the dup elements into vec and delete them from L. When it2 reach the end of L, we call L.erase(it). After that, the process continues until the vector L is empty.
Here is the code that I modified. The most important thing to note is the condition in the while. I use while(it < L.end()) instead of while(it != L.end()) because when we erase an element, the iterator will somehow go pass the condition !=.
I am using an old compiler (older than C++0x) for I don't want to mess with the environment right now. Thus, it will be safe for anyone to use my code.
vector<unsigned> L;
vector<unsigned>::iterator it, it2, it3, it4;
vector<unsigned> vec; //temporary variable to store matched elements
float Z;
// populate the vector (1, 2, 3, 4, 1, 3)
for(unsigned i = 1; i <= 4; ++i)
L.push_back(i);
for(unsigned i = 1; i <= 4; i = i + 2)
L.push_back(i);
it = L.begin();
while(it < L.end()){
cout<< "*it = " << *it << endl;
Z=0;
vec.clear();
vec.push_back(*it);
if(L.size() == 1){
cout << "Last element of vector = " << *it <<endl;
goto Loop1;
}
else
it2 = it + 1;
while(it2 < L.end()){
cout << "Loop *it2 = " << *it2 <<endl; //debug
if(*it == *it2){
vec.push_back(*it2);
L.erase(it2); // iterator automatically points to the next element
cout<< "after erase(it2), *it2 = " << *it2 << endl; //debug
continue;
}
++it2;
}
Loop1:
for(it3 = vec.begin(); it3 != vec.end(); ++it3)
Z = Z+ *it3;
Z= Z/vec.size();
cout<< "Z = " << Z << endl ;
if(L.empty()) break;
L.erase(it); // iterator automatically points to the next element
//debug
cout<< endl << "new vector = ";
for(it4 = L.begin(); it4 != L.end(); ++it4)
cout << *it4 << ' ';
cout << endl;
}
#PaulMcKenzie: I took me a while to catch on with your writing style in C++14 and the use of the STL functions which I am not familiar with. Your code is at very high level and it is hard for beginner. Anyway, I chose it as best answer for your contribution. I really learned a lot from you. Thank you for your quick responses.
I've been googling for this for so long but I couldn't get the answer. The most of sample that I found are based on iterating with vector, map and etc..
I have the code below.
multimap<int, int>::iterator it = myMuliMap.find(1);
Let's say I have three pairs that has key "1". I like to get those three pair from for loop.. I think that I can't use for(multimap::iterator anotherItr=myMuliMap.begin()..
The following code is in C#.. I like to get C++ version.. Thanks.
foreach(var mypair in it){
Console.WriteLine(mypair.Key);
}
The function you're looking for is equal_range. This returns an iterator to all pairs in the map which match the specified key
auto range = myMultiMap.equal_range(1);
for ( auto it = range.first; it != range.second; ++it) {
...
}
EDIT
Version without auto
pair<multimap<int,int>::const_iterator,multimap<int,int>::const_iterator>> it = myMultiMap.equal_range(1);
for ( multimap<int,int>::const_iterator it = range.first;
it != range.second;
++it) {
...
}
Use std::equal_range():
int tolookfor = 1;
typedef multimap<int, int>::iterator iterator;
std::pair<iterator, iterator> p =
std::equal_range(myMuliMap.begin(), myMuliMap.end(), tolookfor);
for (iterator it = p.first; it != p.second ++it)
std::cout << (*it).second << std::endl;
the multi_map's member function equal_range works similarily:
std::pair<iterator, iterator> p =
myMuliMap.equal_range(tolookfor);
This will print out only the values found by
std::pair<std::multimap<int, int>::iterator, std::multimap<int, int>::iterator> result;
result = myMultimap.equal_range(1);
for(std::multimap<int,int>::iterator it = result.first; it != result.second; it++)
{
std::cout << it->first << " = " << it->second << std:: endl;
}
You can use something like the following loop.
for (std::multimap<int, int>::iterator i = myMultiMap.lower_bound(1);
i != myMultiMap.upper_bound(1);
++i)
{
std::cout << i->first << " => " << i->second << '\n';
}
This is valid in the current version of C++.