This seems like a simple problem, and it is certainly doable, but I'd like to do it efficiently.
The Objective:
Remove the last element from a std::list if it meets a condition.
The Problem:
My compiler (MSVC++ 10) is unhappy about casting a reverse iterator to a const iterator for a method call to std::list.erase(). The message is:
error C2664: 'std::_List_iterator<_Mylist>
std::list<_Ty>::erase(std::_List_const_iterator<_Mylist>)' : cannot
convert parameter 1 from 'std::reverse_iterator<_RanIt>' to
'std::_List_const_iterator<_Mylist>'
The Code I Tried:
std::list<mytype> mylist;
// lots of code omitted for clarity
bool ends_badly = true;
while(ends_badly && mylist.size() > 0)
{
auto pos = mylist.crbegin(); // Last element in the list
if ((*pos)->Type() == unwanted)
{
mylist.erase(pos); // Here is where the compiler complains
}
else
{
ends_badly = false;
}
}
I can get around this by using forward iterators and looping through the list to the end, but that's so cumbersome. The compiler is OK with a forward iterator in this context, and I tried casting a the reverse iterator to a const iterator but the compiler didn't like that either.
Erasing a list element from a bidirectional list using a reverse iterator seems like a reasonable thing. Is there something obvious I'm missing here?
I suppose that you can simplify your code snippet doing it the next way:
while (!mylist.empty() && mylist.back()->Type() == unwanted) {
mylist.pop_back();
}
To fix the specific error in your code Can I convert a reverse iterator to a forward iterator?
mylist.erase((pos+1).base());
Using std::reverse_iterator::base
The base iterator refers to the element that is next (from the std::reverse_iterator::iterator_type perspective) to the element the reverse_iterator is currently pointing to.
Anyway, pop_back is the best choice in your case.
Related
I'm new to C++ STL. I'm writing a function to check whether one vector is an subset of another(Duplicate elements also count) and print 'Yes' or 'No'. I have come up with the following code :
void checkMagazine(vector<string> magazine, vector<string> note) {
vector<string>::iterator a;
for(int i=0;i<note.size();i++)
{
a=find(magazine.begin(),magazine.end(),note[i]);
if(a==magazine.end())
{
printf("No");
return;
}else magazine.erase(a-magazine.begin());
}
printf("Yes");
}
But,I'm getting the following compilation error :
error: no matching function for call to ‘std::vector<std::__cxx11::basic_string<char>
>::erase(__gnu_cxx::__normal_iterator<std::__cxx11::basic_string<char>*,
std::vector<std::__cxx11::basic_string<char> > >::difference_type)’
}else magazine.erase(a-magazine.begin());
I'm trying to use erase to delete the found element in magazine.Is there some problem with the type of declaration of the iterator? Why is this happening?
And also if there are alternate methods/logic/inbuilt-functions to get the required job done using STL, Please let me know since I'm new to this and trying to learn it.
erase takes an iterator. You have that iterator, it's called a. But you then convert the iterator a into an offset relative to begin(). erase does not have an overload which takes an offset.
The Standard Library has alternatives if the vectors would be sorted, but we don't see the calling code so we can't assume that.
In my class I am trying to remove an element from a std::vector using a for loop. However, when I try to remove the element, I receive an error and I am not quite sure how to solve it. The error I get is:
Error 4 error C2679: binary '+' : no operator found which takes a right-hand operand of type 'Enemy *' (or there is no acceptable conversion)
void Enemy::UpdateEnemies(SDL_Renderer * render)
{
for (int i = enemies.size() - 1; i >= 0; i--)
{
enemies[i]->Update();
if (enemies[i]->Active == false)
{
// Receive the error here
enemies.erase(enemies.begin() + enemies.at(i));
}
}
if ((SDL_GetTicks()-prevSpawnTime)/1000.0f > enemySpawnTime)
{
prevSpawnTime = SDL_GetTicks();
//Add an anemy
AddEnemy(render);
}
}
Other answers have given you the naive solution. However, if you have more than one enemy to remove, you need a better solution.
Using std::remove_if from <algorithm> would be better in this case. That will avoid repeated shuffling of the items in your vector. It works by moving all the ones you want to remove to the end of the container, and then giving you an iterator to the beginning of those.
auto removed_iter = std::remove_if( enemies.begin(), enemies.end(),
[]( const Enemy * e ) { return e->IsActive(); } );
enemies.erase( removed_iter, enemies.end() );
In this case you would have to update all your enemies first. If that doesn't need to be done in reverse order, then:
for( auto e : enemies ) e->Update();
Assuming that you want to remove the i-th element, you need to do
enemies.erase(enemies.begin() + i);
or better
enemies.erase(std::next(enemies.begin(), i));
In your case enemies.at(i) returns the dereferenced iterator at position i, i.e. an element of type Enemy, and not i or the iterator for position i.
A better way is to use reverse iterators:
for(auto it = enemies.rbegin(); it != enemies.rend(); ++it)
{
(*it)->Update();
if ((*it)->Active == false)
{ // need `base()` to convert to regular iterator, then substract 1
enemies.erase(std::prev(it.base())); // remove the current position
/* or, equivalently
enemies.erase(std::next(it).base());
*/
}
}
This line:
enemies.erase(enemies.begin() + enemies.at(i));
enemise.at(i) returns the Enemy that is stored in the vector.
enemies.begin() is a pointer
As the error says, you are trying to add pointer and vector.
You probably want just to call:
enemies.erase(enemies.begin() + i);
In addition to what others have said, I would take it a step further and suggest that erasing from a vector within a loop could cause undefined behavior. I don't see a break, so I assume that there could be multiple inactive enemies. Instead of writing your own loop, consider using the std::remove_if algorithm. Essentially your code is an attempt to add an iterator with an object reference which will fail to compile. The remove_if solution will essentially copy all enemies where Active==false to the end of the container while shifting everything else forward. It provides a convenient way to first identify the things to remove, and then erase them all at once. Additionally if you don't have a C++11 compiler the same thing will work if you use a different kind of predicate. The remove_if link contains an example of a function, but you can also use a functor.
enemies.erase(std::remove_if(enemies.begin(), enemies.end(), [](const Enemy* e){ return e->Active == false; }), enemies.end());
For more information check these out.
What is a lambda expression in C++11?
http://www.cplusplus.com/reference/algorithm/remove_if/
C++ Functors - and their uses
I have a class Board, with an operator==, and a class Graph, containing a Board* (let's call it tab) and a vector<Graph*> (children).
I have 2 vector<Graph*>, named opened and closed.
How can i look every children in an element of opened, and add a child to opened if this child is not already in closed?
Here is my attempt, but it doesn't compile.
for (vector<Graph*>::iterator itr = opened[0]->getchildren().begin(); itr != opened[0]->getchildren().end(); ++itr) {
// this doesn't compile
vector<Graph*>::iterator it = find(closed.begin(), closed.end(), *itr);
if(it != closed.end())
{
opened.push_back(*it);
}
}
I get:
no matching function for call to 'find(std::vector<Graph*>::iterator, std::vector<Graph*>::iterator, Graph*&)'
I don't really understand how std::find works. But I'm open to every method.
std::find returns an iterator, not a pointer to an iterator:
vector<Graph*>::iterator* it = NULL;
↑
// that line doesn't compile
it = find(closed.begin(), closed.end(), (*itr)->tab);
Your it is a pointer. You just want the actual iterator type:
vector<Graph*>::iterator it = find(...);
You check validity not by comparing against NULL but rather by comparing against the end iterator that you pass in:
if (it != closed.end())
{
...
}
Given the error you just provided, the issue is that find() is looking for a specific value by using operator==. You are looking for a Board* in a vector of Graph*. Those two are not comparable - you can only look for a Graph*. Unless you are looking for a Graph* that contains a Board*, in which case you would want to provide your own predicate via std::find_if
Concerning find, you have to check against the end iterator
it = find(closed.begin(), closed.end(), (*itr)->tab);
if (it != closed.end())
{
...
Other than the obvious error of comparing it against NULL instead of closed.end(), it seems that you want to look for a node of type Graph*.
std::find(start, finish, key) uses the iterators from first to last and invokes operator== on each element against 'key'. The 'things' stored in the vector and key have to be of the same type. Compiler is reporting they are not.
Key is of type Board*, while vector elements are Graph*.
I gave up on std::find and did two loops to compare opened and closed. Thanks for the help.
How to get the last element of an std::unordered_map?
myMap.rbegin() and --myMap.end() are not possible.
There is no "last element" in a container that is unordered.
You might want an ordered container, e.g. std::map and access the last element with mymap.rbegin()->first (Also see this post)
EDIT:
To check if your iterator is going to hit the end, simply increment it (and possibly save it in a temporary) and check it against mymap.end(), or, even cleaner : if (std::next(it) == last)
In your comments, it appears your goal is to determine if you are on the last element when iterating forward. This is a far easier problem to solve than finding the last element:
template<class Range, class Iterator>
bool is_last_element_of( Range const& r, Iterator&& it ) {
using std::end;
if (it == end(r)) return false;
if (std::next(std::forward<Iterator>(it)) == end(r)) return true;
return false;
}
the above should work on any iterable Range (including arrays, std containers, or custom containers).
We check if we are end (in which case, we aren't the last element, and advancing would be illegal).
If we aren't end, we see if std::next of us is end. If so, we are the last element.
Otherwise, we are not.
This will not work on iterators that do not support multiple passes.
You cant. by definition, the element is not stored based on some sort of order. the key is hashed first and that's why O(1) search is possible. if you wanna check whether a key exists in the unordered_map or not, u can use this code:
std::unordered_map dico;
if(dico.count(key)!=0){
//code here
}
std::unordered_map::iterator last_elem;
for (std::unordered_map::iterator iter = myMap.begin(); iter != myMap.end(); iter++)
last_elem = iter;
// use last_elem, which now points to the last element in the map
This will give you the last element in whatever order the map gives them to you.
Edit: You need to use std::unordered_map<YourKeyType, YourValueType> instead of just std::unordered_map. I just wrote it like this because you did not provide the type in your question.
Alternatively, as suggested by vsoftco (thanks), you could declare both last_elem and iter as decltype(myMap)::iterator.
(If you're compiling with the MSVC++ compiler, then you will need to add typedef decltype(myMap) map_type; and then instead of decltype(myMap)::iterator use map_type::iterator.)
.end() is an iterator to the "element past the last element". That's why you compare it like this when you loop through a map:
for (auto it = myMap.begin(); it != myMap.end(); ++it) // '!=' operator here makes it possible to only work with valid elements
{
}
So you want the "last" element (whatever that may be, because it's not really guaranteed to be the last in an unordered map, since it ultimately depends on how the key was hashed and in which "bucket" it ends up in). Then you need: --myMap.end()
More specifically, .end() is a function, that returns an iterator, same as .begin() returns an iterator. Since there is no .rbegin() in an std::unordered_map, you have to use -- (the decrement operator):
auto it = --myMap.end();
To access the key you use it->first, to access the value you use it->second.
The accepted answer seems wrong. Unordered_map does have the last element even though the key-value pair is not stored in sorted order. Since the iterator of unorered_map is forwar_iterator(LegacyForwardIterator), the cost to find the last element is O(n). Yakk - Adam gave the correct answer. Essentially, you have to iterator the container from begin to end. At each iteration, you have to check whether the next element is end(); if yes then you are at the last element.
You cannot call prev(it) or --it. There will be no syntax error, but you will have a runtime error (more likely segmentation fault) when using the prev(it) or --it. Maybe next version of compiler can tell you that you have an logic error.
It may not be the best solution, performance-wise, but in C++11 and later, I use a combination of std::next() and size() to jump all elements from the beginning of the map, as shown below:
std::unordered_map<int,std::string> mapX;
...
if (mapX.size() > 0) {
std::unordered_map<int,std::string>::iterator itLast =
std::next(mapX.begin(), mapX.size() - 1);
...
I have a list and when iterating over it i want to access the element at the iterator and the following element at the iterator+1 at the same time. Here is my attempt:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator iterator = teamlist.begin(); iterator != teamlist.end(); std::advance(iterator,2))
{
match(*(*iterator), *(*(++iterator)));
}
The match function does nothing to the iterators, it just get's some values of the teams out of it to calculate a match result.
But the ++iterator does not work, as the elements in the matchfunction are still the same. How do I do this?
Passing the iterator and operating on the iterator in the same pass (like increment of the iterator) leads to undefined behavior. There are certain concepts such as sequence points, which you should be aware of when you are performing such operations.
Further, you can check this link also. I suggest you to move the increment of the operator after the pass to function. Then it should work fine.
You could avoid increasing the iterator at increment part of the loop and dot it in the body:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator it = teamlist.begin();
it != teamlist.end(); /*Nothing here*/)
{
match(*(*it), *(*(++it))); //Which parameter expression is evaluated first isn't determined
++it;
...
...
EDIT:
As FredOverflow has pointed out, match parameter expressions evaluations are not guaranteed to run in the left to right order. To avoid this hazardous situation:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator it = teamlist.begin();
it != teamlist.end(); /*Nothing here*/)
{
Team *pa = *it;
Team *pb = *(++it);
match(*pa, *pb);
++it;
...
...
you are increasing the iterator twice, first in the head of the for-loop:
std::advance(it,2)
then in the loop-body, where you do a:
++it
Is this really what you want? It looks pretty confusing to me.
If you want the element next to it, but dont want to increase it, better use:
auto nextIt = std::next(it);
Also: What does the match-function do? Are you sure its implemented right and not the source of the bug?
Hope this helps
Alexander