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
typedef
s 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:
typedef
s requisauto
plutôt que de dépendre de l'itérateur pour déclarer les typesstd::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 ?
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.
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).
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;}
};
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.