web-dev-qa-db-fra.com

Préparation pour std :: iterator étant obsolète

Le 21 marsst le comité des normes a voté pour approuver la dépréciation de std::iterator proposé dans P0174 :

La longue séquence d'arguments void est beaucoup moins claire pour le lecteur que de simplement fournir les typedefs attendus dans la définition de classe elle-même, ce qui est l'approche adoptée par le projet de travail actuel, suivant le modèle défini dans c ++ 14

Avant c ++ 17 héritage de std::iterator a été encouragé à retirer l'ennui de l'implémentation du passe-partout de l'itérateur. Mais la dépréciation nécessitera l'une de ces choses:

  1. Un passe-partout d'itérateur devra désormais inclure tous les typedefs requis
  2. Les algorithmes travaillant avec les itérateurs devront désormais utiliser auto plutôt que de dépendre de l'itérateur pour déclarer les types
  3. Loki Astari a suggéré que std::iterator_traits peut être mis à jour pour fonctionner sans hériter de std::iterator

Quelqu'un peut-il m'éclairer sur laquelle de ces options je dois m'attendre, car je conçois des itérateurs personnalisés avec un oeil vers la compatibilité c ++ 17 ?

57
Jonathan Mee

Les alternatives discutées sont claires mais je pense qu'un exemple de code est nécessaire.

Étant donné qu'il n'y aura pas de substitut de langage et sans compter sur boost ou sur votre propre version de la classe de base de l'itérateur, le code suivant qui utilise std::iterator sera fixé au code ci-dessous.

Avec std::iterator

template<long FROM, long TO>
class Range {
public:
    // member typedefs provided through inheriting from std::iterator
    class iterator: public std::iterator<
                        std::forward_iterator_tag, // iterator_category
                        long,                      // value_type
                        long,                      // difference_type
                        const long*,               // pointer
                        const long&                // reference
                                      >{
        long num = FROM;
    public:
        iterator(long _num = 0) : num(_num) {}
        iterator& operator++() {num = TO >= FROM ? num + 1: num - 1; return *this;}
        iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
        bool operator==(iterator other) const {return num == other.num;}
        bool operator!=(iterator other) const {return !(*this == other);}
        long operator*() {return num;}
    };
    iterator begin() {return FROM;}
    iterator end() {return TO >= FROM? TO+1 : TO-1;}
};

(Code de http://en.cppreference.com/w/cpp/iterator/iterator avec la permission de l'auteur d'origine).

Sans pour autant std::iterator

template<long FROM, long TO>
class Range {
public:
    class iterator {
        long num = FROM;
    public:
        iterator(long _num = 0) : num(_num) {}
        iterator& operator++() {num = TO >= FROM ? num + 1: num - 1; return *this;}
        iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
        bool operator==(iterator other) const {return num == other.num;}
        bool operator!=(iterator other) const {return !(*this == other);}
        long operator*() {return num;}
        // iterator traits
        using difference_type = long;
        using value_type = long;
        using pointer = const long*;
        using reference = const long&;
        using iterator_category = std::forward_iterator_tag;
    };
    iterator begin() {return FROM;}
    iterator end() {return TO >= FROM? TO+1 : TO-1;}
};
31
Amir Kirsh

L'option 3 est une version strictement plus typée de l'option 1, car vous devez tout de même écrire typedefs mais également envelopper iterator_traits<X>.

L'option 2 n'est pas viable comme solution. Vous pouvez déduire certains types (par exemple reference est juste decltype(*it)), mais vous ne pouvez pas déduire iterator_category. Vous ne pouvez pas faire la différence entre input_iterator_tag Et forward_iterator_tag Simplement par la présence d'opérations car vous ne pouvez pas vérifier par réflexe si l'itérateur satisfait la garantie multipass. De plus, vous ne pouvez pas vraiment faire la distinction entre ceux-ci et output_iterator_tag Si l'itérateur fournit une référence mutable. Ils devront être explicitement fournis quelque part.

Cela laisse l'option 1. Je suppose que nous devrions juste nous habituer à écrire tous les passe-partout. Pour ma part, je souhaite la bienvenue à nos nouveaux suzerains du canal carpien.

30
Barry