Подготовка к std::iterator устарела

21 мартаst комитет по стандартам проголосовал за прекращение поддержки std::iterator. предложено в P0174:

Длинная последовательность аргументов void гораздо менее понятна читателю, чем просто предоставление ожидаемых typedef в самом определении класса, что является подходом, принятым в текущем рабочем проекте, следуя шаблону, установленному в c++14

До c++17 рекомендуется наследование от std::iterator, чтобы убрать скуку из шаблонной реализации итератора. Но устаревание потребует одну из этих вещей:

  1. Шаблон итератора теперь должен включать все необходимые typedef
  2. Алгоритмы, работающие с итераторами, теперь должны будут использовать auto, а не зависеть от итератора для объявления типов.
  3. Локи Астари предположил, что std::iterator_traits может быть обновлен для работы без наследования от std::iterator

Может ли кто-нибудь просветить меня, какой из этих вариантов мне следует ожидать, поскольку я разрабатываю пользовательские итераторы с прицелом на c++17?


person Jonathan Mee    schedule 04.05.2016    source источник
comment
@FirstStep Я надеюсь получить ответ, который не будет основан на мнении. Если комитет по стандартам осуждает класс, от которого я завишу в следующем году, я надеюсь, что у них будет направление, в котором они направляют меня прямо сейчас.   -  person Jonathan Mee    schedule 04.05.2016
comment
То, что они устарели, не означает, что вы не можете продолжать использовать его какое-то время.   -  person Jesper Juhl    schedule 04.05.2016
comment
Примечание. deprecating означает, что он больше не рекомендуется, но по-прежнему будет поддерживаться по крайней мере для другой версии стандарта.   -  person Martin York    schedule 04.05.2016
comment
Итераторы в стандартной библиотеке выбрали вариант 1.   -  person Bo Persson    schedule 04.05.2016
comment
@BoPersson Казалось бы, тогда мы должны сделать это?   -  person Jonathan Mee    schedule 04.05.2016
comment
@LokiAstari - это еще слабее. Формально устаревание — это уведомление о том, что что-то может исчезнуть в будущем. Это все. Обратите внимание, что стандартные заголовки C устарели в C++ с 1998 года.   -  person Pete Becker    schedule 04.05.2016
comment
@PeteBecker Но все же это это стандартный комитет, пытающийся правильно увести нас от этих вещей. Я имею в виду, что я использовал #include <c*>, пока пишу на C++ из-за этого. Не говорите мне, что это было напрасно!   -  person Jonathan Mee    schedule 04.05.2016
comment
@JonathanMee - нет, это было не напрасно. В этом нет необходимости, но в этом есть свои преимущества.   -  person Pete Becker    schedule 04.05.2016
comment
@PeteBecker И они говорят что-то подобное здесь, верно? Вы использовали это в течение долгого времени, но есть лучший способ. Отсюда вопрос...   -  person Jonathan Mee    schedule 04.05.2016
comment
Вариант 1. Вещь не является итератором, если только iterator_traits не имеет различных определений типов членов, и нет смысла специализироваться на iterator_traits - это больше типизация.   -  person T.C.    schedule 04.05.2016
comment
@JonathanMee - я использую std::iterator, потому что это удобно. Я буду продолжать использовать его, пока не смогу.   -  person Pete Becker    schedule 04.05.2016
comment
@PeteBecker Я уважаю это, но если комитет по стандартам решил, что нам пора двигаться дальше, я готов начать переход.   -  person Jonathan Mee    schedule 04.05.2016
comment
@PeteBecker: мне было интересно, может ли специализация шаблона iterator_traits использовать наследование для сокращения ввода? Стоило бы это усилий, если бы это уменьшило шаблон?   -  person Martin York    schedule 04.05.2016
comment
@LokiAstari Да, неопределенное поведение включает видимость работы.   -  person T.C.    schedule 04.05.2016
comment
@T.C.: Спасибо. Стоит ссылки. :-)   -  person Martin York    schedule 04.05.2016


Ответы (2)


Обсуждаемые альтернативы понятны, но я чувствую, что необходим пример кода.

Учитывая, что не будет заменителя языка и не полагаясь на повышение или на вашу собственную версию базового класса итератора, следующий код, использующий 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;}
};
person Amir Kirsh    schedule 29.06.2016
comment
@AmirKirsh operator* должен возвращать reference, вам нужно, чтобы operator-> возвращал pointer, даже если это не имеет смысла для long - person Ryan Haining; 17.11.2016
comment
Я нашел довольно фантастическую статью об этом здесь , в котором излагаются обоснования. - person Jason C; 18.12.2020

Вариант 3 является строго более типизированной версией Варианта 1, так как надо писать все те же typedefs но дополнительно оборачивать iterator_traits<X>.

Вариант 2 как решение нежизнеспособен. Вы можете вывести некоторые типы (например, reference — это просто decltype(*it)), но вы не можете вывести iterator_category. Вы не можете различать input_iterator_tag и forward_iterator_tag просто по наличию операций, поскольку вы не можете рефлекторно проверить, удовлетворяет ли итератор гарантии многопроходности. Кроме того, вы не можете отличить их от output_iterator_tag, если итератор выдает изменяемую ссылку. Они должны быть где-то явно указаны.

Остается вариант 1. Думаю, нам нужно просто привыкнуть к написанию всего шаблонного. Я, например, приветствую наших новых повелителей запястного канала.

person Barry    schedule 04.05.2016
comment
Если вам действительно нравится то, что делает std::iterator, вы можете тривиально написать свою собственную версию. Так что риск запястного канала сильно преувеличен. - person T.C.; 04.05.2016
comment
@Barry Кажется, что iterator_tag является ключевым, но это немного расстраивает, поскольку его можно определить по наличию или отсутствию операторов const, индекса и декремента, верно? - person Jonathan Mee; 04.05.2016
comment
@JonathanMee Нет. Это итератор ввода или вывода? - person Yakk - Adam Nevraumont; 04.05.2016
comment
Барри, вариант 3 можно рассматривать как вариант 2, но iterator_traits сделает всю работу за вас. Он не работает с типом тега. - person Yakk - Adam Nevraumont; 04.05.2016
comment
@Yakk Разве вы не можете определить это по тому, возвращают ли его значения const? - person Jonathan Mee; 04.05.2016
comment
@JonathanMee Вы не можете отличить ввод/вперед, и прямой итератор ни в коем случае не требуется, чтобы дать вам ссылку const ... - person Barry; 04.05.2016
comment
@Yakk ForwardIterator является InputIterator, если тип, возвращаемый при его отмене, не равен const, тогда это просто OutputIterator, а не InputIterator - person Jonathan Mee; 04.05.2016
comment
И ввод, и вывод не должны возвращать значения или ссылки, если я помню тот причудливый угловой случай. Но я думаю, что это крайний случай. - person Yakk - Adam Nevraumont; 04.05.2016
comment
@JonathanMee В этом нет никакого смысла. - person Barry; 04.05.2016
comment
@ Барри Подожди, как это не имеет смысла? Если класс итератора не предоставляет T& operator*(), только const T& operator*() const, то это OutputIterator, это InputIterator. - person Jonathan Mee; 05.05.2016
comment
Итераторы вывода @Jonathan не могут разыменовывать const T& - они должны быть доступны для записи. Они должны поддерживать *it++ = x; - person Barry; 05.05.2016
comment
@Barry Sooo... почему мы не можем отключить это? - person Jonathan Mee; 05.05.2016
comment
@ Джонатан От чего? Входной итератор и прямой итератор также могут разыменовывать неконстантную ссылку lvalue. - person Barry; 05.05.2016
comment
@Barry Обратите внимание, что OutputIterator также является InputIterator, если возвраты разыменований InputIterator не являются const. Это то, от чего я отключаюсь. - person Jonathan Mee; 05.05.2016
comment
@Jonathan Итератор вывода не обязательно должен быть итератором ввода. - person Barry; 05.05.2016
comment
Ми и en.cppreference.com так и думали: en.cppreference.com/w/cpp/iterator У вас есть контрпример? - person Jonathan Mee; 05.05.2016
comment
@Jonathan Нигде в этой ссылке не утверждается, что итератор вывода является итератором ввода. - person Barry; 05.05.2016
comment
OutputIterator — это итератор, который может записывать в указанный элемент. Помогите мне здесь. Вы говорите, что итератор вывода не обязательно должен быть итератором ввода. Приведите пример. - person Jonathan Mee; 05.05.2016
comment
@JonathanMee Чувак. Итератор и итератор ввода не эквивалентны. - person Barry; 05.05.2016
comment
не требуется много дани-тоннеля, если вы используете boost::iterator_facade или boost::iterator_adaptor - person TemplateRex; 11.05.2016
comment
@TemplateRex Это была шутка. Несмотря на это, кажется глупым отказываться от std::iterator в пользу... теперь каждый пишет свою собственную копию std::iterator, чтобы решить эту проблему. - person Barry; 11.05.2016
comment
связанные: stackoverflow.com/q/29108958/819272 В стандарте более общая тенденция удалять глупые базовые классы, содержащие только typedefs (unary_function и т.д.) - person TemplateRex; 11.05.2016
comment
@JonathanMee Итератор может возвращать константный прокси-объект, с помощью которого вы по-прежнему можете управлять базовым контейнером. Таким образом, const не является надежным индикатором для итераторов ввода-вывода. - person ; 16.05.2016
comment
Как насчет использования boost::iterator_facade ? - person alfC; 11.10.2016