为什么 output_iterator 的概念不需要 output_iterator_tag?

7

C++20引入了标准库中不同类型迭代器的适当概念(输入、输出、前向、双向、随机访问等)。

尽管这些类型的原始命名要求根本未提及{{link1:std::iterator_traits的迭代器标签}},但新的C++20概念明确要求它们。例如,参见input_iterator概念({{link2:[iterator.concept.input]}}):

template<class I>
  concept input_iterator =
    input_or_output_iterator<I> &&
    indirectly_readable<I> &&
    requires { typename ITER_CONCEPT(I); } &&
    derived_from<ITER_CONCEPT(I), input_iterator_tag>;

注意最后一行对迭代器标签的检查。所有迭代器概念都像这样检查相应的标记,比如除输出迭代器之外。在这方面,输出迭代器一直是特殊的自 Ranges TS 的早期以来

与 C++ 标准中的输出迭代器要求不同, Ranges TS 中的 OutputIterator 不要求定义迭代器类别标记。

为什么要对输出迭代器进行这种特殊处理呢?
1个回答

4
在C++20中,迭代器类别通常基于语法自动检测。显式使用迭代器标签仅用于选择退出或选择进入:
- 一切比random_access_iterator_tag弱的都用于选择退出更强的迭代器类别(例如,输入迭代器在语法上与前向迭代器无法区分); - contiguous_iterator_tag用于选择进入contiguous_iterator,因为连续性是相对罕见的属性,也无法通过语法检测。
输出迭代器不需要这种细粒度的控制 - 要么你可以写入它,要么不能,这是通过语法检测确定的。
关于何时提供选择加入/退出的选项,Casey Carter解释如下:
  • 向限制参数的库组件传递参数是隐含的声明,即该参数不满足约束条件(不符合语法要求),或者它符合语法和语义要求,因此通常不需要选择加入/退出。

  • 只有当我们想要使库假定模型某个概念X的参数在满足Y时还满足X的某个细分Y时,才会提供选择加入/退出的功能。(我一直称之为“语义提升”,但这不是一个已经确立的术语。)选择加入还是退出取决于错误阳性预期频率。

“语义提升”通常在非输出迭代器参数上完成。输出迭代器没有任何可以提升的内容。


我还不被 opt-out 的论点所说服。Forward、Bidirectional 和 RandomAccess 都有不同的语法要求,这使它们彼此区分开来:Bidirectional 没有 ++= 运算符,forward 没有 -- 运算符。如果它们可以通过语法区分,为什么我需要额外的机制呢?而且,即使在满足语法要求时有 opt-out 的理由,为什么现在这个理由不适用于输出迭代器呢? - ComicSansMS
也无法在语法上检测到” 实际上,C++20的contiguous_iterator概念有一些可以在语法上进行关键操作的内容。具体来说,将迭代器转换为指针的函数(我相信它被称为to_address)。 - Nicol Bolas
@ComicSansMS: “为什么现在这个原因不同样适用于输出迭代器呢?” 因为iteratoroutput_iterator具有可检测的语法要求。因此,一个概念可以自动检测到一个并防止另一个。而在input_iteratorforward_iterator之间不存在这种情况;它们之间的差异全部是语义上的(复制一个的含义等等)。 - Nicol Bolas
@NicolBolas 只要你可以为其形成一个 pointer_traits 特化,to_address 就适用于任何带有 -> 的东西。 - T.C.

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