for_each 在自定义双向迭代器的 reverse_iterator 上需要 OutputIterator
for_each on a reverse_iterator of a custom bidirectional iterator requires 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';
}
迭代器是不可变的,因为 operator*() returns 一个 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
关于 reverse_iterator 和 for_each 的 cppreference 页面声明两者分别需要一个双向迭代器和一个输入迭代器。我认为这两个要求都得到了满足,但是 stl 仍然为引用分配了一个解除引用的值。
为什么 stl 的 for_each/reverse_iterator 期望迭代器上的 T &operator*() 不需要是 OutputIterator?
PS:注释行可以通过声明引用应按值存储来解决问题,当然非常 hacky。
它不需要 OutputIterator。问题是您的代码违反了所有输入迭代器的基本要求*: *r
must return reference
**。您的代码将 my_iterator::reference
定义为 int &
(由于 std::iterator
的默认模板参数),但是 operator*
return 是 int
.
有效reference
实际上不是引用类型(istreambuf_iterator<charT>::reference
,例如charT
),但operator*
必须return reference
。 reverse_iterator
依赖于此,因为它定义了它的 reference
成员,因此它的 operator*
的 return 类型定义为它的包装迭代器的 reference
.
根据标准,对于前向迭代器或更强的迭代器,reference
必须是引用类型。但是当它调用 vector<bool>::iterator
一个随机访问迭代器(它的 operator*
必须 return 一个代理)时,标准本身就撒谎了,并且委员会显然计划在 array_view
proposal.因此,虽然 my_iterator::reference
int
意味着它在技术上不再是双向迭代器,但实际上它可能会起作用。希望通过 Concepts,我们可以获得比目前更好、更细粒度的需求。
* 关于输出迭代器的标准存在矛盾。见 LWG issue 2437.
** 从技术上讲,std::iterator_traits<It>::reference
。对于 class 类型,iterator_traits
默认遵循成员 typedef It::reference
.
所有迭代器的迭代器要求列于[iterator.iterators]:
reference
指的是iterator_traits<my_iterator<..>>
的typedef:
In the following sections, a
and b
denote values of type X
or
const X
, difference_type
and reference
refer to the types
iterator_traits<X>::difference_type
and
iterator_traits<X>::reference
, respectively, [..]
由于 iterator_traits
的主模板只是将 typedef 默认为模板参数本身定义的类型,我们正在谈论 my_iterator
的 reference
typedef - 而且一个是从基础 std::iterator<...>
继承的,默认为 T&
.
你的 operator*
returns 和 int
肯定不是 int&
.
取消注释行对于 InputIterators 没问题,因为 int
可以转换为 int
:
虽然 ForwardIterators 失败了 - [forward.iterators]/1:
A class or pointer type X
satisfies the requirements of a forward
iterator if
— if X
is a mutable iterator, reference
is a reference to T
;
if X
is a const iterator, reference
is a reference to const T
,
我创建了一个简单的不可变双向迭代器:
#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*() returns 一个 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
关于 reverse_iterator 和 for_each 的 cppreference 页面声明两者分别需要一个双向迭代器和一个输入迭代器。我认为这两个要求都得到了满足,但是 stl 仍然为引用分配了一个解除引用的值。
为什么 stl 的 for_each/reverse_iterator 期望迭代器上的 T &operator*() 不需要是 OutputIterator?
PS:注释行可以通过声明引用应按值存储来解决问题,当然非常 hacky。
它不需要 OutputIterator。问题是您的代码违反了所有输入迭代器的基本要求*: *r
must return reference
**。您的代码将 my_iterator::reference
定义为 int &
(由于 std::iterator
的默认模板参数),但是 operator*
return 是 int
.
有效reference
实际上不是引用类型(istreambuf_iterator<charT>::reference
,例如charT
),但operator*
必须return reference
。 reverse_iterator
依赖于此,因为它定义了它的 reference
成员,因此它的 operator*
的 return 类型定义为它的包装迭代器的 reference
.
根据标准,对于前向迭代器或更强的迭代器,reference
必须是引用类型。但是当它调用 vector<bool>::iterator
一个随机访问迭代器(它的 operator*
必须 return 一个代理)时,标准本身就撒谎了,并且委员会显然计划在 array_view
proposal.因此,虽然 my_iterator::reference
int
意味着它在技术上不再是双向迭代器,但实际上它可能会起作用。希望通过 Concepts,我们可以获得比目前更好、更细粒度的需求。
* 关于输出迭代器的标准存在矛盾。见 LWG issue 2437.
** 从技术上讲,std::iterator_traits<It>::reference
。对于 class 类型,iterator_traits
默认遵循成员 typedef It::reference
.
所有迭代器的迭代器要求列于[iterator.iterators]:
reference
指的是iterator_traits<my_iterator<..>>
的typedef:
In the following sections,
a
andb
denote values of typeX
orconst X
,difference_type
andreference
refer to the typesiterator_traits<X>::difference_type
anditerator_traits<X>::reference
, respectively, [..]
由于 iterator_traits
的主模板只是将 typedef 默认为模板参数本身定义的类型,我们正在谈论 my_iterator
的 reference
typedef - 而且一个是从基础 std::iterator<...>
继承的,默认为 T&
.
你的 operator*
returns 和 int
肯定不是 int&
.
取消注释行对于 InputIterators 没问题,因为 int
可以转换为 int
:
虽然 ForwardIterators 失败了 - [forward.iterators]/1:
A class or pointer type
X
satisfies the requirements of a forward iterator if— if
X
is a mutable iterator,reference
is a reference toT
; ifX
is a const iterator,reference
is a reference toconst T
,