iterator_category和iterator_concept之间有什么区别?

14
C++20带来了一个更强大的迭代器系统,其中之一是在迭代器类别的基础上引入了iterator_concept。
我发现C++20中许多迭代器的iterator_concept和iterator_category不一致。以最著名的iota_view为例。
using R = decltype(views::iota(0));
static_assert(random_access_range<R>);

using I = ranges::iterator_t<R>;
static_assert(same_as<typename I::iterator_category, input_iterator_tag>);
static_assert(same_as<typename I::iterator_concept,  random_access_iterator_tag>);

尽管R模型random_access_range,但其迭代器的iterator_category只是一个input_iterator_tag,与iterator_concept不一致。
为什么C++20引入iterator_concept?它的目的是什么?如果我实现自己的迭代器,如何正确定义iterator_concept和iterator_category?在C++20中,iterator_category还有意义吗?
1个回答

25
C++17(C++98)迭代器模型和C++20 Ranges迭代器模型存在不兼容之处。两个主要的区别是:
  1. C++98模型要求前向迭代器具有reference,其类型必须为value_type&value_type const&
  2. C++98模型不允许使用contiguous迭代器。最强的类别是random_access
第一个区别的后果非常重要-这意味着如果您有一个返回prvalue的迭代器(无论是代理引用还是其他),它永远不能比输入迭代器更强大。因此,views::iota(1, 10)尽管可以轻松支持随机访问,但最多只能成为C++98输入范围。
然而,您不能仅仅删除这个要求。现有代码假定C++98迭代器并使用iterator_category来进行判断,这是完全合理的,假设iterator_categorybidirectional_iterator_tag,那么它的reference就是某种左值引用到value_typeiterator_concept添加了一个新的C++20层,允许迭代器同时声明其C++98/17类别和C++20类别。所以回到iota_view<int, int>的例子,该视图的迭代器将iterator_category设置为input_iterator_tag(因为reference是prvalue,因此它不满足旧要求甚至不具备前向迭代器),但其iterator_concept设置为random_access_iterator_tag(因为一旦我们放弃该限制,我们就可以轻松支持所有随机访问限制)。
[iterator.concepts.general]中,我们有这个神奇的函数ITER_CONCEPT(I),它帮助我们确定在C++20中使用什么标记。
第二个区别的问题在于,由于各种C++98/17代码会检查那个标签(许多代码可能会检查确切的random_access_iterator_tag),所以很难直接添加一个新的contiguous_iterator_tagiterator_concept方法通过引入直接检查正确事物的概念(即random_access_iterator概念检查ITER_CONCEPT(I)是否派生自random_access_iterator_tag,而不是简单地是那个)。

指南:

  • 如果您在使用C++17中的迭代器,请使用std::iterator_traits<I>::iterator_category
  • 如果您在使用C++20中的迭代器,请使用std::meow_iterator概念。
  • 如果您在编写C++17中的迭代器,请添加iterator_category别名,并确保遵循前向迭代器/引用限制(或者不遵循,但这是您的责任)。
  • 如果您在编写C++20中的迭代器,请遵循P2259中的指导,其中有关于问题以及何时如何提供iterator_categoryiterator_concept类型别名的良好描述。

1
@康桓瑋 这确实不一致,但我认为这并没有太大的影响(我不知道库是否关心这种情况,例如 V 是随机访问,但 V const 是前向访问。像...为什么?)更典型的情况是,V 是随机访问,但 V const 不是一个范围,这种区别就不那么重要了。 - Barry
但是如果 V 没有提供 begin() const 函数,则由于 transform_view::begin() const 的约束条件,将不会实例化 transform_view::iterator<const>。我仍然无法理解 const 在此处可能引起的问题。 - 康桓瑋
1
@康桓瑋,请停止使用马甲账号给Barry声望,这会让他得意忘形的。 - Yakk - Adam Nevraumont
@barry,他们给你点了100个赞!“+100”:我的意思是,要有所克制。 - Yakk - Adam Nevraumont
1
这是LWG3555 - 康桓瑋
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接