2 votes

Mettre en œuvre un itérateur const cpp plante et brûle

De nombreux articles sur les itérateurs constants (exemple), mais aucun dans le contexte des boucles comme:

for (const auto it: container) {...}

Lorsque j'ai commencé à implémenter, le compilateur m'a encouragé par les plaignants sur les begin et end manquants, en espérant que de tels plaignants puissent m'aider à combler les pièces manquantes. J'avais tort. Le code suivant se compile bien même s'il manque sûrement l'équivalent de operator++ et operator!=:

#include 
template class List { public:
const T *head;
const List *tail;
List(const T *h, const List *t):head(h),tail(t){}
const T *operator*() const { return head; } };

template const List *begin(const List *l) { return l; }
template const List *end  (const List *l) { return nullptr; }

class Person { public: int age; Person(int a):age(a){} };
typedef List Persons;

int main(int argc, char **argv) {
Person *p1 = new Person(16);
Person *p2 = new Person(27);
Person *p3 = new Person(38);

Persons *persons = new Persons(p1,
new Persons(p2,
new Persons(p3, nullptr)));

for (const auto p: persons) { std::cout << (*p)->age << "\n"; }
return 0; }

Comment se fait-il que ce code se compile?

4voto

jtbandes Points 39804

Vous n'avez pas défini ++ et !=, mais ils sont parfaitement définis pour votre type d'itérateur, const List*, car il s'agit d'un type pointeur. Cependant, le comportement par défaut de ++ ne fait pas ce que vous voulez, qui serait de suivre le pointeur tail.

Pour doter votre itérateur de la connaissance spéciale de la façon dont ++ devrait être implémenté, vous voudriez utiliser une classe d'itérateur séparée et personnalisée, plutôt que d'utiliser un pointeur.

J'ai également apporté d'autres modifications à votre code :

  • Le type List est rendu itérable, pas List*. Cela signifie que les fonctions begin et end ne sont pas définies sur des pointeurs, mais sur l'objet de la liste lui-même, et nous itérons sur *persons plutôt que sur persons.
  • Dans for (const auto p : *persons), p est un Person, pas un pointeur.

lien godbolt.org

#include 
template class List { public:
    const T *head;
    const List *tail;
    List(const T *h, const List *t):head(h),tail(t){}
    const T *operator*() const { return head; }

    class const_iterator {
        const List* cur = nullptr;
    public:
        explicit const_iterator(const List* liste) : cur(liste) {}
        const_iterator& operator++() {
            cur = cur->tail;
            return *this;
        }
        bool operator!=(const const_iterator& autre) const {
            return cur != autre.cur;
        }
        const T& operator*() const {
            return *cur->head;
        }
    };

    const_iterator begin() const {
        return const_iterator(this);
    }

    const_iterator end() const {
        return const_iterator(nullptr);
    }
};

class Personne { public: int age; Personne(int a):age(a){} };
typedef List Personnes;

int main(int argc, char **argv) {
    Personne *p1 = new Personne(16);
    Personne *p2 = new Personne(27);
    Personne *p3 = new Personne(38);

    Personnes *personnes = new Personnes(p1,
        new Personnes(p2,
        new Personnes(p3, nullptr)));

    for (const auto p: *personnes) {
        std::cout << p.age << "\n";
    }
    return 0;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X