c++ - 为什么从采用 std::ranges::output_range 的算法返回 std::ranges::safe_iterator_t 而不是 std::ranges::safe_subrange_t

标签 c++ iterator range c++20

我正在编写一种算法,将一些数据写入提供的输出范围(问题的初始文本包括具体细节,这将评论中的讨论转向了错误的方向)。我希望它在 API 中尽可能接近标准库中的其他范围算法。

我查看了 std::ranges::output_range 实例的最新草案,只发现了 2 种算法:

它们都返回std::ranges::safe_iterator_t。我认为返回 std::ranges::safe_subrange_t 是合乎逻辑的。即使您写入输出流,在这种情况下您仍然可以返回迭代器-哨兵对并将该范围向下传递。

我找到了P0970看起来 std::ranges::safe_subrange_t 是后来添加的。也许算法根本没有更新?还是有其他原因?

最佳答案

safe_iterator_t的存在在范围设计中可以归因于两件事:

  1. 某些算法将迭代器返回到传递给算法的范围内,并且
  2. 有些范围的迭代器生命周期可能超过其范围,而有些则没有。

对于 (2),一个示例可能是 std::string_view 。即使在string_view之后,进入字符串 View 的迭代器仍然可以使用。对象本身已被破坏。那是因为string_view只是引用内存中其他位置的元素,而 string_view对象本身不包含额外的附加状态。反例是任何容器;例如,std::vector ,它拥有它的元素,以及 C++20 std::ranges 中的许多 View 。命名空间,其中大部分包含附加状态(例如 views::filter 的谓词)。

将上面的两个项目符号放在一起,现在考虑像 find 这样的函数(简化):

template <input_range R, class T>
  requires ...
safe_iterator_t<R> find(R && rg, const T & val);

此函数返回一个迭代器到 rg 范围内,但是如果rg是一个右值,那么当函数返回时它可能会被删除。这意味着返回的迭代器几乎肯定是悬空的。

safe_iterator_t检查是否 R是迭代器可以安全地超出范围的特殊范围类型之一。如果是这样,你只需取回迭代器即可,没有麻烦,没有大惊小怪。如果不是,则此函数返回一个名为 std::ranges::dangling 的特殊类型的空对象。 。这是为了让您了解这样一个事实:您需要更深入地思考这里的生命。

相同的逻辑适用于采用输出范围的算法,例如 ranges::fillranges::generate .

那么为什么不返回 safe_subrange_t而不是safe_iterator_t ,你可能会问?这难道不会使该算法与其他算法很好地结合吗?

会的!但是它会返回他们已经拥有的调用者信息;即范围结束的位置。在算法中,我们避免做不必要的工作,以使它们尽可能高效。给定 ABI 和调用约定,返回一个指针(例如,找到的位置)比返回包含两个指针(例如,找到的位置和范围末尾)的结构更有效。

相反,我们使用更高级别的 View (以及 range-v3 中的操作)来简洁地组合多个操作。

关于c++ - 为什么从采用 std::ranges::output_range 的算法返回 std::ranges::safe_iterator_t 而不是 std::ranges::safe_subrange_t,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57860924/

相关文章:

php - 网络 - 视频 : bytes range to time

c++ - 将 webm 视频从 URL 流式传输到 C++ windows.h 应用程序

swift - Swift 中的循环迭代器

f# - 在 f# 中是否有用于测试一个数字是否在其他两个数字之间(在一个范围内)的语言结构?

php - 为什么 ArrayIterator 子类的构造函数从未被调用?

java - 为什么要返回 Iterator 低耦合 (OOP)?

javascript - 生成连续值之间具有递增/递减差异的整数序列

c++:数组,初始化程序太多

c++ - 代码中什么定义了c++版本?

c++ - GLSL - 给定顶点法线计算表面法线