自定义双向迭代器的reverse_iterator上的for_each需要OutputIterator

时间:2015-02-20 14:29:32

标签: c++ c++11 stl c++14 reverse-iterator

我创建了一个简单的不可变双向迭代器:

#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';
}

迭代器是不可变的,因为operator *()返回 int 而不是 int reference 。根据我的理解,这是可能的,因为迭代器是否符合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

关于reverse_iterator和for_each状态的cppreference页面分别需要BidirectionalIterator和InputIterator。我认为这两个要求都得到了满足,但是stl仍然为引用分配了一个取消引用的值。

为什么stl的for_each / reverse_iterator期望迭代器上的T&amp; operator *()不需要是OutputIterator?

PS:注释行可以通过声明引用应该按值存储来修复问题,当然非常hacky。

2 个答案:

答案 0 :(得分:7)

它不需要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 proposal来处理更多内容。因此,虽然制作my_iterator::reference int意味着它在技术上不再是双向迭代器,但实际上它可能会起作用。希望通过概念,我们可以获得比我们现有的更好和更细粒度的要求。


* 标准中有关输出迭代器的矛盾。请参阅LWG issue 2437

** 从技术上讲,std::iterator_traits<It>::reference。对于类类型,默认情况下iterator_traits遵循成员typedef It::reference

答案 1 :(得分:5)

所有迭代器的迭代器要求列在[iterator.iterators]中:

enter image description here

reference指的是iterator_traits<my_iterator<..>>的typedef:

  

在以下各节中,ab表示X类型的值或   const Xdifference_type reference指的是类型   iterator_traits<X>::difference_type和    iterator_traits<X>::reference ,分别为[..]

由于iterator_traits的主要模板只是将typedef默认为模板参数本身中定义的类型,我们讨论的是reference my_iterator的typedef - 那个是继承自基础std::iterator<...>,默认为T& 您的operator*会返回int,但肯定不是int&

由于int可转换为int,因此取消注释您的行适用于InputIterators:

enter image description here

虽然对于Forw​​ardIterators失败了 - [forward.iterators] / 1:

  

类或指针类型X满足前锋的要求   迭代器,如果

     

- 如果X是可变迭代器,reference是对T的引用;   如果X是常量迭代器,reference是对const T的引用,