0

I'm having a problem here and I thought I would find an easy answer on the Internet, but it's been 1 hour and I can't solve it. Seems so simple, but I can't find a way...

I've got 2 classes:

#include <iostream>
#include <list>
using namespace std;

class classB;

class classA{
private :
    string name;
    list<classB*> listClassB;

public:
    void getListClassB() const;
};

class classB{
private:
    string name;
    list<classA*> listClassA;

public:
    void getListClassA() const;
};

What I do on the getListClassB() method is:

void classA::getListClassB() const {
    for(list<classB*>::iterator it = listClassB.begin(); it != listClassB.end; it++){
        //Stuff
    }
}

Visual Studio Code tells me that there is an error is on listClassB from list<classB*>::iterator it = listClassB.begin()

The complete error about that is:

there is no appropriate user-defined conversion of 
"std::_List_const_iterator<std::_List_val<std::conditional_t<true, std::_List_simple_types<classB *>, std::_List_iter_types<classB *, size_t, ptrdiff_t, classB **, classB *const *, classB *&, classB *const &, std::_List_node<classB *, void *> *>>>>\" in \"std::_List_iterator<std::_List_val<std::conditional_t<true, std::_List_simple_types<classB *>, std::_List_iter_types<classB *, size_t, ptrdiff_t, classB **, classB *const *, classB *&, classB *const &, std::_List_node<classB *, void *> *>>>>"

EDIT:

Ok, so thanks again for all your time, but this error makes me crazy.

I completed a bit the code to be more explicit about my work.

I don't go deeper because it's college work in France, and it's about UML classes, so it's class linked with others classes...

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
babbou
  • 1
  • 1
  • 3
    Try `(*it)->str` – Passerby Dec 01 '21 at 23:28
  • Side note: `list`s of pointers are not all that useful unless you're storing polymorphic objects. `list` has fantastically forgiving [iterator invalidation rules](https://stackoverflow.com/questions/6438086/iterator-invalidation-rules-for-c-containers) so you don't have to worry about inserts and removals screwing things up so you don't get much in trade for the extra pointer chasing and possibly memory management woes if you're dynamically allocating the `Whatever`s. – user4581301 Dec 01 '21 at 23:32
  • `iterator it = myList.begin()` is correct, the error is in how you dereference. Trying to change how you initialize the iterator is just making it worse. – Nathan Pierson Dec 02 '21 at 00:03
  • 1
    You should put together an actual [mcve] if you're still having trouble with this. In your example there's no `;` at the end of the class definition and no constructor that makes `Whatever wtvr("hey");` work. If I correct both of those issues *and* properly dereference the iterator it works fine for me. https://ideone.com/FelLN9 – Retired Ninja Dec 02 '21 at 00:03
  • 2
    Your function is marked `const`, therefore you must use `const_iterator`, not `iterator`. Otherwise you are breaking the language rules. You are also missing parentheses on your call to `end()` in the loop. – paddy Dec 02 '21 at 00:44
  • Thanks dude it's finally working you're saving me ! – babbou Dec 02 '21 at 00:50
  • You can also just do `auto it = listClassB.begin()` to avoid needing to write the name longhand. – Nathan Pierson Dec 02 '21 at 00:53

2 Answers2

5

First, iterator is a pointer semantic object, so you have to dereference it to access the actual object. it.str makes no sense, since std::list<>::iterator has no str member (and even if it did, that would have nothing to do with str member of Whatever class.

Second, your list elements are pointers themselves, so this means you need a second indirection in order to access the str member:

std::cout << (*(*it)).str;

More appealing syntax would be:

std::cout << (*it)->str;
SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • Thanks for the edit. I did a double-take after your claim that `->` would magically follow a chain of pointers. That was news to me! ;) – paddy Dec 01 '21 at 23:32
  • That's different. My mistake was not noticing the list was storing pointers, since the title of the question was about `list`. I think about 3 other people fell into the same trap! :) – paddy Dec 01 '21 at 23:34
  • Yeppers. Almost closed the question as a typo until I spotted that little complication. – user4581301 Dec 01 '21 at 23:35
  • Wait, why _doesn't_ a single `->` work? I thought that was the point of the `->` rules that allow you to do `std::unique_ptr pFoo; pFoo->fooMember;` – Nathan Pierson Dec 01 '21 at 23:35
  • @paddy `->` does chain dereferencing, but not in a way I used in the example. Looks like I needed a refresher there. – SergeyA Dec 01 '21 at 23:37
  • @NathanPierson But an iterator that references a pointer (`unique_ptr` or other wise) needs two dereferences to get at the object. One to deref the iterator and one to deref the pointer at the iterator. – user4581301 Dec 01 '21 at 23:37
  • Oh, the chain stops because `iterator::operator->` is returning a `Whatever**` not the pointed-to `Whatever*`. – Nathan Pierson Dec 01 '21 at 23:40
  • Sounds like I need to go do some reading on chain dereferncing. It's great how a trivial example from a learner opened up a deeper topic! XD – paddy Dec 01 '21 at 23:40
  • 2
    @NathanPierson in order for `->` to keep dereferencing it should return pointer-like object, not actual pointer - it stops the chain when it actually finds a pointer, and iterator's `operator->` returns a pointer. – SergeyA Dec 01 '21 at 23:41
  • @paddy yeah, chain dereference always seemed a bit counter-intuitive to me, and here was I, embarrassing myself :) – SergeyA Dec 01 '21 at 23:44
  • Thanks for the help but I haven't been clear, sorry :/ I edited my post – babbou Dec 02 '21 at 00:03
1

The error message is difficult to read with all the template stuff in it, but it basically boils down to this:

there is no appropriate user-defined conversion of 
"std::_List_const_iterator<>" in "std::_List_iterator<>"

getListClassB() is marked as const, so its this pointer is typed as const classA *, thus the listClassB member is implicitly const when accessed via this.

When list::begin() is called on a const list object, it returns a list::const_iterator, which your loop is trying to assign to a list::iterator, which is not allowed, hence the error. list::iterator allows the list data to be modified, whereas list::const_iterator does not. Which is why a const_iterator cannot be assigned to an iterator.

That being said, your for loop has another typo in it. You need to change listClassB.end to listClassB.end() instead (notice the extra parenthesis), ie you need to call end() and compare its return value to it, not compare end itself to it.

Try this instead:

void classA::getListClassB() const {
    for(list<classB*>::const_iterator it = listClassB.begin(); it != listClassB.end(); ++it){
        //Stuff
    }
}

Note, however, that the const_iterator/iterator mismatch could have been avoided if you had used auto instead to let the compiler deduce the appropriate type for it, eg:

void classA::getListClassB() const {
    for(auto it = listClassB.begin(); it != listClassB.end(); ++it){
        //Stuff
    }
}

But, a safer way to avoid both mistakes would be to use a range-for loop instead, eg:

void classA::getListClassB() const {
    for(classB *ptr : listClassB){
        //Stuff
    }
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770