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​::​iteratoriterator_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,迭代器Iiterator_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_viewC 将是 std::input_iterator_tag。所以 transform_view 的类别将是 std::input_iterator_tag 不管 你的函子 returns.

当您使用基于 std::ranges 的代码时,您真的应该避免关心 iterator_category。如果你知道你正在处理 C++20 范围,那么你应该使用 iterator_concept.