4

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

#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 引用。据我所知,这是可能的,因为迭代器是否满足 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 期望在不需要成为 OutputIterator 的迭代器上使用 T &operator*()?

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

4

2 回答 2

7

它不需要 OutputIterator。问题是您的代码违反了所有输入迭代器的基本要求**r必须返回reference**。您的代码定义my_iterator::referenceint &(由于std::iterator的默认模板参数),但operator*返回一个int.

reference实际上不是引用类型(istreambuf_iterator<charT>::reference例如 is )是有效的charT,但operator*必须 return referencereverse_iterator依赖于此,因为它定义了它的reference成员,因此它的返回类型operator*,作为它的包装迭代器reference

根据标准,对于前向迭代器或更强大的,reference必须是引用类型。但标准本身在调用vector<bool>::iterator随机访问迭代器时存在缺陷(它operator*必须返回一个代理),并且委员会显然计划在array_view提案中再撒一些谎。因此,虽然 makemy_iterator::reference int意味着它在技术上不再是双向迭代器,但实际上它可能会起作用。希望通过 Concepts,我们可以得到比目前更好、更细粒度的需求。


*关于输出迭代器的标准存在矛盾。请参阅LWG 问题 2437

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

于 2015-02-20T14:35:50.263 回答
5

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

在此处输入图像描述

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

在以下部分中,ab表示类型X或 的值const Xdifference_type并分别reference指代类型 iterator_traits<X>::difference_typeiterator_traits<X>::reference, [..]

由于主模板iterator_traits只是将 typedefs 默认为模板参数本身中定义的类型,因此我们正在讨论 - 的referencetypedef,my_iterator并且它是从 base 继承的std::iterator<...>,它默认为T&.
你的operator*回报int虽然,但肯定不是int&

取消注释您的行对于 InputIterators 来说很好,因为int它可以转换为int

在此处输入图像描述

但是,ForwardIterators 失败了 - [forward.iterators]/1:

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

— ifX是可变迭代器,是对; reference的引用 if是 const 迭代器,是对 的引用TXreferenceconst T

于 2015-02-20T14:55:00.207 回答