我正在编写一种算法,该算法将一些数据写入提供的输出范围(问题的初始文本包括具体内容,并将注释中的讨论变成错误的方向)。我希望它在API中与标准库中的其他范围算法尽可能接近。

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

  • std::ranges::fill
  • std::ranges::generate

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

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

    最佳答案

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

  • 一些算法将迭代器返回到传递到算法的范围内,
  • 有些范围具有迭代器,其迭代器可能超出其范围,而有些则没有。

  • 对于(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 (和 Action ,在range-v3中)来简洁地组成多个操作。

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

    10-11 17:08