So I have a class called symbol, which is made up of 4 strings which are all public. I created a list of these and I want to do a look ahead on this list. This is what I have so far. I looked up the iterator methods and it says it supports the + operator but I get an error for this.
bool parser::parse(list<symbol> myList){
//Will read tokens by type to make sure that they pass the parse
std::list<symbol>::const_iterator lookAhead = myList.begin();
if ((lookAhead + 1) != myList.end)
lookAhead++;
for (std::list<symbol>::const_iterator it = myList.begin(); it != myList.end(); ++it){
if (it->type == "") {
}
}
return true;
}
I get an error when trying to add 1 to lookAhead. What are some good ways of creating a look ahead for a list?
Thanks,
Binx
A linked list does not support random access iterators, i.e. you cannot add an integer to its iterators.
Use std::next(lookAhead) to get the next iterator instead, or std::advance(lookAhead, 1). These functions know what kind of iterator is being passed, and will use a random seek if possible (e.g. with std::vector's random-access iterators), or manually advance (with a loop in the case of std::advance()) otherwise, as in this case.
Be careful advancing on iterators unconditionally, though -- advancing past end() is undefined!
You can read more about the different categories of C++ iterators here.
Side note: You're copying the entire list when it's passed in, since you're passing it by value. You probably want to pass it by reference instead (list<symbol> const& myList). You can also simplify your code using the C++11 auto keyword, which deduces the type automatically from the type of the expression that initializes the variable:
bool parser::parse(list<symbol> const& myList){
// Will read tokens by type to make sure that they pass the parse
auto lookAhead = myList.begin();
if (lookAhead != myList.end() && std::next(lookAhead) != myList.end())
++lookAhead;
for (auto it = myList.begin(); it != myList.end(); ++it){
if (it->type == "") {
}
}
return true;
}
Related
I'm new to C++ and after googling quite a bit I haven't found a solution.
I'm making a Hexgame, and I'm trying to check if there is a winner.
My function checkpath, gets a list of places it has visited (usedPlaces), and creates the list Possible, of places it can check next from the current position.
So I want to delete the places I have already visited from Possible.
The error I'm getting atm is "List iterators incompatible"
I've tried things, but they mostly end up giving me other errors
for (list<string>::iterator it = possible.begin(); it != possible.end(); it++)
{
for (list<string>::iterator it2 = usedPlaces.begin(); it2 != usedPlaces.end();)
{
if (it == it2)
{
possible.remove(*it);
}
else
{
it2++;
}
}
}
When you do this:
if (it == it2)
you're comparing an iterator into one list to an iterator into another list. Those would never compare equal anyway, and it's handy that your implementation debugs this for you. Even if you fixed the comparison (to *it == *it2), the code would still be buggy due to how and when it gets incremented and inefficient (due to the extra searching of possible). A functional version would be:
for (list<string>::iterator it = possible.begin(); it != possible.end(); /* nothing */)
{
list<string>::iterator it2 = std::find(usedPlaces.begin(), usedPlaces.end(), *it);
if (it2 != usedPlaces.end()) {
it = possible.erase(it);
}
else {
++it;
}
}
But this is complicated to write and error-prone. I'd prefer to use list::remove_if, which takes a predicate and removes all the elements for which that predicate returns true:
possible.remove_if([&](const std::string& place){
return std::find(usedPlaces.begin(), usedPlaces.end(), place)
!= usedPlaces.end();
});
That's much more direct.
The goal is to removes string objects that are the same, so the comparison should look at the string objects. Instead of
if (it == it2)
it should be
if (*it == *it2)
The operation it == it2 checks if one iterator points to the same element as the other iterator. That is never true, because those two iterators are coming from two different lists.
What you probably want to compare are the contents of each list element, i.e. *it.
Note, there may be other problems in your code. E.g. after you call possible.remove(*it) your iterator it is no longer referencing any elements of the list, therefore you won't be able to increment it in the next iteration of the loop.
Consider using possible.erase and getting its result. You will need, most likely, change your outher loop though.
Well, I have an iterator over a string in C++
std::string::iterator itB = itC->begin();
And I want to check if the next position of the iterator reaches the end of the line. I've tried this:
if(*itB ++ != NULL)
Also this:
itB++ == itC->end()
But now I'm really confused and I need some help, since I'm a rookie at pointers in C++.
You want to check it without modifying it. Both of your attempts involve modifying itB. If you have C++11, that's just std::next:
std::next(itB) == itC->end()
If you don't, just make another iterator:
std::string::iterator next = itB;
++next;
next == itC->end()
Although, in this case specifically we know that std::string::iterator is a random access iterator, so we can also just do:
itB + 1 == itC->end()
(The previous two work for any iterator category)
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.
I have this for:
for (list<Docente>::iterator it3 = ((*it2).getDocentes()).begin(); it3 != ((*it2).getDocentes()).end(); it3++)
This ((*it2).getDocentes) returns a list of Docentes. So why does it say that list iterators are incompatible? Any help?
Method getDocentes() :
list <Docente> getDocentes() const;
list <Docente> EdicaoDisciplina :: getDocentes() const
{
return docentes;
}
Error:
Expression: list iterators incompatible
You haven't provided enough information.
If you get that typical MS Visual C++ run-time assertion about incompatible iterators, then I suspect that your getDocentes() returns the list by value, i.e. it returns a temporary copy of the original container. That means that begin() is called on one temporary copy and end() is called on a completely different temporary copy. That makes the iterators returned by begin() and end() incompatible, since it is illegal to compare iterators that point into different containers. Debug versions of some run-time libraries can catch such errors and issue run-timer assertion failures.
It is also possible that the same problem happens even earlier, because of *it2 returning its result by value.
EDIT: Just as I guessed, your getDocentes() returns its result by value. This is virtually meaningless and catastrophically inefficient. Such heavy objects as lists are not supposed to be passed around by value. Return it by reference
const list <Docente>& EdicaoDisciplina :: getDocentes() const
{
return docentes;
}
Now you have to use const_iterator instead of iterator (and lose all these excessive pairs of ())
for (list<Docente>::const_iterator it3 = it2->getDocentes().begin();
it3 != it2->getDocentes().end();
++it3)
...
Finally, evaluating it2->getDocentes().end() on every iteration is not a good idea either
for (list<Docente>::const_iterator
it3 = it2->getDocentes().begin(),
it3_end = it2->getDocentes().end();
it3 != it3_end;
++it3)
...
Every time you call getDocentes() you're getting back a new list, and you can't validly compare iterators across different STL containers.
In your case, there is a list that gets returned when you call getDocentes().begin(), and then there is a new list created for every loop iteration, when you call getDocentes().end().
You could either use a range-based for loop, if you have C++11, or you could just cache the begin and end iterators before entering you for loop:
list <Docente> myDocentes(getDocentes());
list <Docente>::iterator begin = myDocentes.begin();
list <Docente>::iterator end = myDocentes.end();
for(list<Docente>::iterator i = begin; i != end; i++) { .... }
Or, just use the range based for:
for(auto &it : getDocentes()) { ... }
I am trying to delete an element from a list of objects if one of the object's properties matches a condition. This is my function to do so, however, after performing this operation and then printing the contents, the erase() seems to have no effect. What am I doing wrong here?
void FileReader::DeleteProcess(int id, list<Process> listToDeleteFrom)
{
list<Process>::iterator process;
for(process = listToDeleteFrom.begin(); process != listToDeleteFrom.end(); process++)
{
if (process -> ID == id)
{
listToDeleteFrom.erase(process);
}
}
}
First, you need to pass the list by reference; your code is working on a copy, so changes it makes won't affect the caller's list:
void FileReader::DeleteProcess(int id, list<Process> & listToDeleteFrom)
^
Second, erasing a list element invalidates any iterator that refers to that element, so attempting to carry on iterating afterwards will cause undefined behaviour. If there will only be one element to remove, then return from the function straight after the call to erase; otherwise, the loop needs to be structured something like:
for (auto it = list.begin(); it != list.end(); /* don't increment here */) {
if (it->ID == id) {
it = list.erase(it);
} else {
++it;
}
}
Calling erase() when an iterator is iterating over the list invalidates the iterator. Add the elements to erase to a second list then remove them afterwards.
Also note that you are passing the list by value rather than using a reference or a pointer. Did you mean to use list<Process>& listToDeleteFrom or list<Process>* listToDeleteFrom?
The reason you see no changes reflected is that your list is not being passed by reference, so you are only removing elements from a copy of the list.
Change it to this:
void FileReader::DeleteProcess(int id, list<Process> &listToDeleteFrom) //note &
This will keep the same syntax in the function and modify the original.
However, the way you're deleting the elements is a bit sub-optimal. If you have C++11, the following will remove your invalidation problem, and is more idiomatic, using an existing algorithm designed for the job:
listToDeleteFrom.erase ( //erase matching elements returned from remove_if
std::remove_if(
std::begin(listToDeleteFrom),
std::end(listToDeleteFrom),
[](const Process &p) { //lambda that matches based on id
return p->ID == id;
}
),
std::end(listToDeleteFrom) //to the end of the list
);
Note the keeping of std::list<>::erase in there to actually erase the elements that match. This is known as the erase-remove idiom.