Для for_each обратного_тератора пользовательского двунаправленного итератора требуется OutputIterator

Я создал простой неизменный двунаправленный итератор:

#include <iostream>
#include <memory>
#include <iterator>
#include <vector>
#include <algorithm>

class my_iterator : public std::iterator<std::bidirectional_iterator_tag, int
//, std::ptrdiff_t, int*, int
> {
int d_val;
public:
my_iterator() : d_val(0) {}
my_iterator(int val) : d_val(val) {}

my_iterator  operator--(int) { d_val--; return my_iterator(d_val + 1); }
my_iterator &operator--()    { d_val--; return *this; }
my_iterator  operator++(int) { d_val++; return my_iterator(d_val - 1); }
my_iterator &operator++()    { d_val++; return *this; }

int operator*() const { return d_val; }

bool operator==(my_iterator const  &o) { return d_val == o.d_val; }
bool operator!=(my_iterator const  &o) { return d_val != o.d_val ; }
};int main() {
std::reverse_iterator<my_iterator> reverse_it_begin(25);
std::reverse_iterator<my_iterator> reverse_it_end(12);
std::for_each(reverse_it_begin, reverse_it_end, [](int e){ std::cout << e << ' '; });
std::cout << '\n';
}

Итератор неизменен, так как оператор * () возвращает ИНТ вместо Int ссылка. Насколько я понимаю, это возможно, поскольку соответствует ли итератор концепции BidirectionalIterator или концепция OutputIterator ортогональна (возможны все 4 комбинации).

Однако приведенный ниже код приводит к ошибке времени компиляции, а именно:

/usr/include/c++/4.9/bits/stl_iterator.h:164:9: error: invalid initialization of non-const reference of type 'std::reverse_iterator<my_iterator>::reference {aka int&}' from an rvalue of type 'int'

Полный контекст:

In file included from /usr/include/c++/4.9/bits/stl_algobase.h:67:0,
from /usr/include/c++/4.9/bits/char_traits.h:39,
from /usr/include/c++/4.9/ios:40,
from /usr/include/c++/4.9/ostream:38,
from /usr/include/c++/4.9/iostream:39,
from prog.cpp:1:
/usr/include/c++/4.9/bits/stl_iterator.h: In instantiation of 'std::reverse_iterator<_Iterator>::reference std::reverse_iterator<_Iterator>::operator*() const [with _Iterator = my_iterator; std::reverse_iterator<_Iterator>::reference = int&]':
/usr/include/c++/4.9/bits/stl_algo.h:3755:6:   required from '_Funct std::for_each(_IIter, _IIter, _Funct) [with _IIter = std::reverse_iterator<my_iterator>; _Funct = main()::<lambda(int)>]'
prog.cpp:30:86:   required from here
/usr/include/c++/4.9/bits/stl_iterator.h:164:9: error: invalid initialization of non-const reference of type 'std::reverse_iterator<my_iterator>::reference {aka int&}' from an rvalue of type 'int'
return *--__tmp;
^

Success time: 0 mem

Страницы на cppreference о состоянии reverse_iterator и for_each, для которых требуется BidirectionalIterator и InputIterator соответственно. Я думаю, что оба требования выполнены, однако stl по-прежнему назначает разыменованное значение для ссылки.

Почему stl for_each / reverse_iterator ожидает T &оператор * () на итераторе, который не должен быть OutputIterator?

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

4

Решение

Требования к итераторам для всех итераторов перечислены в [iterator.iterators]:

введите описание изображения здесь

reference относится к typedef из iterator_traits<my_iterator<..>>:

В следующих разделах a а также b обозначать значения типа X или же
const X, difference_type а также reference обратитесь к типам
iterator_traits<X>::difference_type а также
iterator_traits<X>::reference, соответственно [..]

Поскольку основной шаблон iterator_traits просто по умолчанию typedefs для типов, определенных в самом аргументе шаблона, мы говорим о reference typedef of my_iterator — и этот унаследован от базы std::iterator<...>по умолчанию это T&,
Ваш operator* возвращает int хотя, что, конечно, не int&,

Раскомментирование вашей строки хорошо для InputIterators, так как int конвертируется в int:

введите описание изображения здесь

Это не работает для ForwardIterators, хотя — [forward.iterators] / 1:

Класс или тип указателя X удовлетворяет требованиям форварда
итератор, если

— если X изменчивый итератор, reference это ссылка на T;
если X является постоянным итератором, reference это ссылка на const T,

5

Другие решения

Это не требует OutputIterator. Проблема в том, что ваш код нарушает основное требование для всех входных итераторов*: *r должен вернуться reference**. Ваш код определяет my_iterator::reference как int & (из-за std::iteratorшаблонный аргумент по умолчанию), но operator* возвращает int,

Это действительно для reference на самом деле не быть ссылочным типом (istreambuf_iterator<charT>::referenceнапример charT), но operator* должен вернуться reference, reverse_iterator опирается на это, так как он определяет его reference член, и, следовательно, тип возврата его operator*, как завернутый итератор reference,

Согласно стандарту, для прямых итераторов или сильнее, reference должен быть ссылочным типом. Но сам стандарт лежит, когда он вызывает vector<bool>::iterator итератор произвольного доступа (его operator* должен вернуть доверенность), и комитет, по-видимому, планирует еще array_view предложение. Так что, делая my_iterator::reference int будет означать, что технически это больше не двунаправленный итератор, на практике это может сработать. Надеемся, что с Concepts мы сможем получить более качественные и детальные требования, чем те, которые у нас есть в настоящее время.


* В стандарте есть противоречие в отношении выходных итераторов. Увидеть Выпуск LWG 2437.

** Технически, std::iterator_traits<It>::reference, Для типов классов iterator_traits по умолчанию относится к элементу typedef It::reference,

7

По вопросам рекламы [email protected]