transform_view ::iterator的iterator_category定义的问题?
The problem of the definition of transform_view::iterator's iterator_category?
标准在[range.adaptors]中定义了多种范围适配器,其中一些有自己的迭代器类型。
为了规范这些迭代器的iterator_category
,标准还规定了它们的定义方式。例如[range.transform.iterator-2]中transform_view::iterator
的iterator_category
定义如下:
The member typedef-name iterator_category
is defined if and only
if Base
models forward_range
. In that case,
iterator::iterator_category
is defined as follows: Let C
denote
the type iterator_traits<iterator_t<Base>>::iterator_category
.
If is_lvalue_reference_v<invoke_result_t<F&, range_reference_t<Base>>>
is true
, then
if C
models
derived_from<contiguous_iterator_tag>
, iterator_category
denotes random_access_iterator_tag
;
otherwise, iterator_category
denotes C
.
Otherwise,iterator_category
denotes input_iterator_tag
.
不过这个定义好像有些问题,考虑following case:
vector v{1, 2, 3, 4, 5};
auto r = views::iota(0, 5) |
views::transform([&](int i) -> int& { return v[i]; });
using I = ranges::iterator_t<decltype(r)>;
static_assert(random_access_iterator<I>);
static_assert(same_as<I::iterator_concept, random_access_iterator_tag>);
static_assert(__detail::__cpp17_randacc_iterator<I>);
static_assert(same_as<I::iterator_category, input_iterator_tag>);
由于r
在C++20中是完美的random_access_range
,它的迭代器模型random_access_iterator
,然而,根据上面的描述,由于iota_view::iterator_category
只是input_iterator_tag
,迭代器I
的iterator_category
也只有input_iterator_tag
.
但是显然迭代器I
满足了LegacyRandomAccessIterator
的所有要求,所以它的iterator_category
应该是random_access_iterator
.
更合理
这是标准缺陷吗?或者这背后有什么考虑?
根据 C++20,transform_view
is determined as follows 的迭代器类别:
iterator::iterator_category
is defined as follows: Let C
denote the type iterator_traits<iterator_t<Base>>::iterator_category
.
- If
is_lvalue_reference_v<invoke_result_t<F&, range_reference_t<Base>>>
is true, then
- if
C
models derived_from<contiguous_iterator_tag>
, iterator_category
denotes random_access_iterator_tag
;
- otherwise,
iterator_category
denotes C
.
- Otherwise,
iterator_category
denotes input_iterator_tag
.
因此,如果您的仿函数 return 是左值引用,则迭代器类别派生自基本迭代器类型的迭代器类别。而提供基础迭代器的基础范围是views::iota
.
但是,因为 iota
是纯右值范围(它的迭代器 return 纯右值,而不是引用),its iterator_category
cannot be anything other than std::input_iterator_tag
。所以在上面的文本中 transform_view
,C
将是 std::input_iterator_tag
。所以 transform_view
的类别将是 std::input_iterator_tag
, 不管 你的函子 returns.
当您使用基于 std::ranges
的代码时,您真的应该避免关心 iterator_category
。如果你知道你正在处理 C++20 范围,那么你应该使用 iterator_concept
.
标准在[range.adaptors]中定义了多种范围适配器,其中一些有自己的迭代器类型。
为了规范这些迭代器的iterator_category
,标准还规定了它们的定义方式。例如[range.transform.iterator-2]中transform_view::iterator
的iterator_category
定义如下:
The member typedef-name
iterator_category
is defined if and only ifBase
modelsforward_range
. In that case,iterator::iterator_category
is defined as follows: LetC
denote the typeiterator_traits<iterator_t<Base>>::iterator_category
.If
is_lvalue_reference_v<invoke_result_t<F&, range_reference_t<Base>>>
istrue
, then
if
C
modelsderived_from<contiguous_iterator_tag>
,iterator_category
denotesrandom_access_iterator_tag
;otherwise,
iterator_category
denotesC
.Otherwise,
iterator_category
denotesinput_iterator_tag
.
不过这个定义好像有些问题,考虑following case:
vector v{1, 2, 3, 4, 5};
auto r = views::iota(0, 5) |
views::transform([&](int i) -> int& { return v[i]; });
using I = ranges::iterator_t<decltype(r)>;
static_assert(random_access_iterator<I>);
static_assert(same_as<I::iterator_concept, random_access_iterator_tag>);
static_assert(__detail::__cpp17_randacc_iterator<I>);
static_assert(same_as<I::iterator_category, input_iterator_tag>);
由于r
在C++20中是完美的random_access_range
,它的迭代器模型random_access_iterator
,然而,根据上面的描述,由于iota_view::iterator_category
只是input_iterator_tag
,迭代器I
的iterator_category
也只有input_iterator_tag
.
但是显然迭代器I
满足了LegacyRandomAccessIterator
的所有要求,所以它的iterator_category
应该是random_access_iterator
.
这是标准缺陷吗?或者这背后有什么考虑?
根据 C++20,transform_view
is determined as follows 的迭代器类别:
iterator::iterator_category
is defined as follows: LetC
denote the typeiterator_traits<iterator_t<Base>>::iterator_category
.
- If
is_lvalue_reference_v<invoke_result_t<F&, range_reference_t<Base>>>
is true, then
- if
C
modelsderived_from<contiguous_iterator_tag>
,iterator_category
denotesrandom_access_iterator_tag
;- otherwise,
iterator_category
denotesC
.- Otherwise,
iterator_category
denotesinput_iterator_tag
.
因此,如果您的仿函数 return 是左值引用,则迭代器类别派生自基本迭代器类型的迭代器类别。而提供基础迭代器的基础范围是views::iota
.
但是,因为 iota
是纯右值范围(它的迭代器 return 纯右值,而不是引用),its iterator_category
cannot be anything other than std::input_iterator_tag
。所以在上面的文本中 transform_view
,C
将是 std::input_iterator_tag
。所以 transform_view
的类别将是 std::input_iterator_tag
, 不管 你的函子 returns.
当您使用基于 std::ranges
的代码时,您真的应该避免关心 iterator_category
。如果你知道你正在处理 C++20 范围,那么你应该使用 iterator_concept
.