C++20 引入了ranges::borrowed_range
,它定义了一个范围的要求,这样一个函数可以按值获取它并返回从它获得的迭代器,而不会有悬空的危险。简而言之(参考P2017R1):
当范围超出范围后您可以保留其迭代器时,范围就是一个借用范围。
同时,borrowed_subrange_t
还引入了一个类型助手:
template<ranges::range R>
using borrowed_subrange_t = std::conditional_t<
ranges::borrowed_range<R>,
ranges::subrange<ranges::iterator_t<R>>,
ranges::dangling
>;
这是一个别名模板,由一些受约束的算法使用,例如ranges::unique
和ranges::find_end
避免返回潜在的悬空迭代器或视图。
当 type R
modelsborrowed_range
时,borrowed_subrange_t
ofR
基本上是 a subrange<ranges::iterator_t<R>>
,这意味着它也是 a ranges::common_range
,因为它只接受一个模板参数,而第二个参数默认与第一个参数相同。
但是似乎有些误导,因为有些subrange
类型可以借用但仍然不是common_range
,请考虑以下代码:
auto r = views::iota(0);
auto s1 = ranges::subrange{r.begin(), r.begin() + 5};
auto s2 = ranges::subrange{r.begin() + 5, r.end()};
我subrange
从 a 创建了两个 s borrowed_range
ranges::iota_view
,一个包含前 5 个元素,另一个包含itoa_view
从第五个元素开始的所有元素。它们是subrange
s 的itoa_view
,而且它们显然是借来的:
static_assert(ranges::borrowed_range<decltype(s1)>);
static_assert(ranges::borrowed_range<decltype(s2)>);
所以在某种程度上,它们的类型都可以看成是borrowed_subrange_t
类型的itoa_view
,但是根据定义,只有类型s1
是borrowed_subrange_t
类型r
,这也意味着下面的代码是非良构的,因为iota_view
r
它不是一个common_range
:
auto bsr = ranges::borrowed_subrange_t<decltype(r)>{r}; // ill-formed
为什么标准需要保证borrowed_subrange_t
of some range
R
is a common_range
,即begin()
and的返回类型end()
相同?这背后的原因是什么?为什么不更一般地定义它:
template <ranges::range R>
using borrowed_subrange_t = std::conditional_t<
ranges::borrowed_range<R>,
ranges::subrange<
ranges::iterator_t<R>,
std::common_iterator<
ranges::iterator_t<R>,
ranges::sentinel_t<R>
>
>,
ranges::dangling
>;
这样做会不会有任何潜在的缺陷和危险?