简体   繁体   English

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

[英]Why return std::ranges::safe_iterator_t instead of std::ranges::safe_subrange_t from algorithms taking std::ranges::output_range

I'm writing an algorithm that writes some data to provided output range (initial text of the question included the specifics and that turned discussion in comments into a wrong direction). 我正在编写一种算法,该算法将一些数据写入提供的输出范围(问题的初始文本包括具体内容,并将注释中的讨论变成错误的方向)。 I want it to be as close as possible in API to other ranges algorithms in the standard library. 我希望它在API中与标准库中的其他范围算法尽可能接近。

I've looked at the latest draft for instances of std::ranges::output_range and found only 2 algorithms: 我查看了std::ranges::output_range实例的最新草案,发现只有2种算法:

And they both return std::ranges::safe_iterator_t . 它们都返回std::ranges::safe_iterator_t I thought it is logical to return std::ranges::safe_subrange_t instead. 我认为返回std::ranges::safe_subrange_t相反是合乎逻辑的。 Even if you write to output stream, you can still return an iterator-sentinel pair in that case and pass that range down the line. 即使您写入输出流,在这种情况下,您仍然可以返回迭代器-前哨对,并将该范围向下传递。

I've found P0970 and it looks like std::ranges::safe_subrange_t was added later. 我找到了P0970 ,它看起来像稍后添加了std::ranges::safe_subrange_t Maybe the algorithms were simply not updated? 也许算法根本没有更新? Or is there a different reason? 还是有其他原因?

The presence of safe_iterator_t in the ranges design can be attributed to two things: 范围设计中是否存在safe_iterator_t可以归结为两点:

  1. Some algorithms return iterators into the range(s) passed into the algorithm, and 某些算法会将迭代器返回到传递到算法的范围中,并且
  2. Some ranges have iterators than can outlive their ranges, and some don't. 有些范围的迭代器超出了其范围的寿命,而有些则没有。

For (2), an example might be std::string_view . 对于(2),示例可能是std::string_view Iterators into the string view are still ok to use even after the string_view object itself has been destroyed. 即使将string_view对象本身销毁后,仍可以使用进入字符串视图的迭代器。 That is because a string_view is just referring to elements elsewhere in memory, and the string_view object itself contains no extra additional state. 这是因为string_view只是引用内存中其他位置的元素,而string_view对象本身不包含任何额外的状态。 Counter-examples would be any of the containers; 反例将是任何一个容器。 eg, std::vector , which owns its elements, and many of the views in C++20's std::ranges namespace, most of which contain additional state ( views::filter 's predicate, for instance). 例如, std::vector ,拥有其元素,以及C ++ 20的std::ranges命名空间中的许多视图,其中大多数包含附加状态(例如, views::filter的谓词)。

Taking the two bullets above together, now consider a function like find (simiplifying): 结合上面的两个项目符号,现在考虑像find (简化)这样的函数:

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

This function returns an iterator into the range rg , but if rg is an rvalue, then it will probably get deleted when the function returns. 该函数将迭代器返回到rg范围内,但是如果rg是一个右值,则该函数返回时可能会被删除。 That means the returned iterator is almost certainly dangling. 这意味着返回的迭代器几乎可以确定是悬空的。

safe_iterator_t checks to see if R is one of those special range type for which the iterators can safely outlive the range. safe_iterator_t检查R是否是迭代器可以安全地超出范围的那些特殊范围类型之一。 If so, you just get the iterator back, no muss no fuss. 如果是这样,您只需将迭代器取回来,就不会大惊小怪。 If not, then this function returns an empty object of a special type named std::ranges::dangling . 如果不是,则此函数返回一个名为std::ranges::dangling的特殊类型的空对象。 That is intended to clue you in to the fact that you need to be thinking more deeply about lifetime here. 这是为了提示您以下事实:您需要在这里更深入地考虑生命。

The same logic holds for algorithms that take output ranges, like ranges::fill and ranges::generate . 采取输出范围的算法(例如ranges::fillranges::generate逻辑相同。

So why not return safe_subrange_t instead of safe_iterator_t , you might ask? 那么,为什么不返回safe_subrange_t而不是safe_iterator_t呢? Wouldn't that make the algorithm compose nicely with other algorithms? 这会使算法与其他算法完美组合吗?

It would! 它会! But it would be returning to the caller information they already have; 但是它将返回到他们已经拥有的呼叫者信息。 namely, the position of the end of the range. 即,范围末端的位置。 In the algorithms, we avoid doing needless work to make them as efficient as possible. 在算法中,我们避免做不必要的工作以使它们尽可能高效。 Given ABIs and calling conventions, returning a pointer (eg, the found position) is more efficient than returning a struct containing two pointers (eg, the found position and the end of the range). 给定ABI和调用约定,返回指针(例如,找到的位置)比返回包含两个指针(例如,找到的位置和范围的结尾)的结构更有效。

Rather, we use higher-level views (and actions, in range-v3) for composing multiple operations concisely. 相反,我们使用更高级别的视图(和动作,在range-v3中)来简洁地组成多个操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM