Подготовка к std::iterator устарела
21 марта комитет по стандартам проголосовал за утверждение амортизации std::iterator
предложено в P0174:
Длинная последовательность пустых аргументов гораздо менее понятна читателю, чем просто предоставление ожидаемого
typedef
s в самом определении класса, который является подходом, принятым текущим рабочим проектом, следуя шаблону, установленному в C++14
До наследования C++17 от std::iterator
Было рекомендовано удалить скуку из стандартной реализации итератора. Но для амортизации потребуется одна из следующих вещей:
- Теперь шаблон итератора должен будет включать все необходимые
typedef
s - Алгоритмы работы с итераторами теперь нужно будет использовать
auto
вместо того, чтобы полагаться на итератор для объявления типов - Локи Астари предположил, что
std::iterator_traits
может быть обновлен для работы без наследования отstd::iterator
Может ли кто-нибудь объяснить мне, какой из этих вариантов мне следует ожидать, когда я разрабатываю собственные итераторы, ориентируясь на совместимость C++17?
2 ответа
Обсуждаемые альтернативы понятны, но я чувствую, что нужен пример кода.
Учитывая, что не будет никакой замены языка и не полагаясь на boost или на вашу собственную версию базового класса итератора, следующий код, который использует std::iterator
будет зафиксирован в коде внизу.
С 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;}
};
(Код с http://en.cppreference.com/w/cpp/iterator/iterator с разрешения автора).
Без 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;}
};
Вариант 3 является строго типизированной версией Варианта 1, так как вы должны писать все то же самое typedefs
но дополнительно завернуть iterator_traits<X>
,
Вариант 2 нежизнеспособен в качестве решения. Вы можете вывести некоторые типы (например, reference
просто decltype(*it)
), но вы не можете вывести iterator_category
, Вы не можете различить input_iterator_tag
а также forward_iterator_tag
просто наличием операций, поскольку вы не можете рефлексивно проверить, удовлетворяет ли итератор многопроходной гарантии. Кроме того, вы не можете действительно различать те и output_iterator_tag
если итератор выдает изменяемую ссылку. Они должны быть явно указаны где-то.
Это оставляет Вариант 1. Думаю, мы должны просто привыкнуть писать весь шаблон. Я, например, приветствую наших новых повелителей запястного канала.