21 мартаst комитет по стандартам проголосовал за прекращение поддержки std::iterator. предложено в P0174 а>:
Длинная последовательность аргументов void гораздо менее понятна читателю, чем просто предоставление ожидаемых typedef в самом определении класса, что является подходом, принятым в текущем рабочем проекте, следуя шаблону, установленному в c++14
До c++17 рекомендуется наследование от std::iterator, чтобы убрать скуку из шаблонной реализации итератора. Но устаревание потребует одну из этих вещей:
- Шаблон итератора теперь должен включать все необходимые
typedef
- Алгоритмы, работающие с итераторами, теперь должны будут использовать
auto, а не зависеть от итератора для объявления типов.
- Локи Астари предположил, что
std::iterator_traits может быть обновлен для работы без наследования от std::iterator
Может ли кто-нибудь просветить меня, какой из этих вариантов мне следует ожидать, поскольку я разрабатываю пользовательские итераторы с прицелом на c++17?
Ответ 1 · рейтинг 66 · Amir Kirsh
Обсуждаемые альтернативы понятны, но я чувствую, что необходим пример кода.
Учитывая, что не будет заменителя языка и не полагаясь на повышение или на вашу собственную версию базового класса итератора, следующий код, использующий 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;}
};
Ответ 2 · рейтинг 33 · Barry
Вариант 3 является строго более типизированной версией Варианта 1, так как надо писать все те же typedefs но дополнительно оборачивать iterator_traits<X>.
Вариант 2 как решение нежизнеспособен. Вы можете вывести некоторые типы (например, reference - это просто decltype(*it)), но вы не можете вывести iterator_category. Вы не можете различать input_iterator_tag и forward_iterator_tag просто по наличию операций, поскольку вы не можете рефлекторно проверить, удовлетворяет ли итератор гарантии многопроходности. Кроме того, вы не можете отличить их от output_iterator_tag, если итератор выдает изменяемую ссылку. Они должны быть где-то явно указаны.
Остается вариант 1. Думаю, нам нужно просто привыкнуть к написанию всего шаблонного. Я, например, приветствую наших новых повелителей запястного канала.