C++20 中 iterator_category 和 iterator_concept 有什么区别?
What is the difference between iterator_category and iterator_concept in C++20?
C++20带来了更强大的iterator系统,其中之一就是在iterator_category
的基础上引入了iterator_concept
。
我发现C++20中很多迭代器的iterator_concept
和iterator_category
不一致。把最著名的iota_view
当成一个example:
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
? iterator_category
在 C++20 中还有意义吗?
C++17 (C++98) 迭代器模型和 C++20 范围迭代器模型之间存在不向后兼容的差异。两个大的是:
- C++98 模型要求前向迭代器有一个
reference
,即 value_type&
或 value_type const&
。
- C++98 模型不允许
contiguous
迭代器。最强类别是 random_access
.
(1) 的结果非常重要——这意味着如果您有一个 returns 纯右值的迭代器(无论代理引用与否),它永远不会比输入迭代器强。因此,views::iota(1, 10)
,尽管很容易支持随机访问,但充其量只是一个 C++98 输入范围。
但是,您不能只是...删除此要求。假设 C++98 迭代器并使用 iterator_category
进行判断的现有代码完全有权假设如果 iterator_category
是 bidirectional_iterator_tag
,那么它的 reference
是对 value_type
.
的某种左值引用
iterator_concept
所做的是添加一个新的 C++20 层,该层允许迭代器既宣传其 C++98/17 类别,又明确地宣传其 C++20 类别。因此回到 iota_view<int, int>
示例,该视图的迭代器已将 iterator_category
设置为 input_iterator_tag
(因为 reference
是纯右值,因此它不满足偶数的旧要求forward) 但它的 iterator_concept
设置为 random_access_iterator_tag
(因为一旦我们放弃该限制,我们就可以轻松支持所有随机访问限制)。
在 [iterator.concepts.general] 中,我们有这个神奇的函数 ITER_CONCEPT(I)
可以帮助我们找出在 C++20 中使用什么标签。
(2) 的问题在于,由于各种 C++98/17 代码检查该标记的方式(很多代码可能准确检查 random_access_iterator_tag
)。 iterator_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
别名并确保遵循前向 iterator/reference 限制(或者......不,但它在你身上)
- 如果您正在编写 C++20 中的迭代器,请遵循 P2259 中的指南,其中对问题以及如何以及何时进行了很好的描述提供
iterator_category
和 iterator_concept
类型别名。
C++20带来了更强大的iterator系统,其中之一就是在iterator_category
的基础上引入了iterator_concept
。
我发现C++20中很多迭代器的iterator_concept
和iterator_category
不一致。把最著名的iota_view
当成一个example:
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
? iterator_category
在 C++20 中还有意义吗?
C++17 (C++98) 迭代器模型和 C++20 范围迭代器模型之间存在不向后兼容的差异。两个大的是:
- C++98 模型要求前向迭代器有一个
reference
,即value_type&
或value_type const&
。 - C++98 模型不允许
contiguous
迭代器。最强类别是random_access
.
(1) 的结果非常重要——这意味着如果您有一个 returns 纯右值的迭代器(无论代理引用与否),它永远不会比输入迭代器强。因此,views::iota(1, 10)
,尽管很容易支持随机访问,但充其量只是一个 C++98 输入范围。
但是,您不能只是...删除此要求。假设 C++98 迭代器并使用 iterator_category
进行判断的现有代码完全有权假设如果 iterator_category
是 bidirectional_iterator_tag
,那么它的 reference
是对 value_type
.
iterator_concept
所做的是添加一个新的 C++20 层,该层允许迭代器既宣传其 C++98/17 类别,又明确地宣传其 C++20 类别。因此回到 iota_view<int, int>
示例,该视图的迭代器已将 iterator_category
设置为 input_iterator_tag
(因为 reference
是纯右值,因此它不满足偶数的旧要求forward) 但它的 iterator_concept
设置为 random_access_iterator_tag
(因为一旦我们放弃该限制,我们就可以轻松支持所有随机访问限制)。
在 [iterator.concepts.general] 中,我们有这个神奇的函数 ITER_CONCEPT(I)
可以帮助我们找出在 C++20 中使用什么标签。
(2) 的问题在于,由于各种 C++98/17 代码检查该标记的方式(很多代码可能准确检查 random_access_iterator_tag
)。 iterator_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
别名并确保遵循前向 iterator/reference 限制(或者......不,但它在你身上) - 如果您正在编写 C++20 中的迭代器,请遵循 P2259 中的指南,其中对问题以及如何以及何时进行了很好的描述提供
iterator_category
和iterator_concept
类型别名。