为什么 C++23 range:: 不将容器类型 C 限制为范围?

问题描述 投票:0回答:2

C++23 引入了非常强大的

ranges::to
,用于从范围构造对象(通常是容器),具有以下定义 ([range.utility.conv.to]):

template<class C, input_­range R, class... Args> requires (!view<C>)
  constexpr C to(R&& r, Args&&... args);

注意,它只是约束模板参数

C
不是
view
,也就是说,
C
甚至可能不是
range

但是,其实现使用

range_value_t<C>
来获取
C
的元素类型,这使得
C
至少是一个
range
,因为模板参数
range_value_t
必须对
R
进行建模,因此
range
约束。

那么,为什么

ranges::to
对模板参数
C
的约束如此松散?

我注意到论文的R3版本曾经将

C
约束为
input_range
,这显然是合理的,因为
input_range
保证了
range_value_t
格式良好,但在R4中这个约束是已删除。而且我没有找到任何关于此更改的评论。

那么,去除

C
必须是
input_range
的约束有哪些考虑?

有没有一个实际例子来说明这种约束放松的好处?

c++ range-v3 std-ranges c++23
2个回答
4
投票

这是我们需要解决的措辞问题,我将在今天晚些时候提出一个问题。这是LWG 3785


那么,去除

C
必须是
input_range
的约束有哪些考虑?

ranges::to
的目标是将范围收集到......某物中。但它不一定是实际范围。只是消耗所有元素的东西。当然,最常见的用法是实际容器类型,最常见的实际容器类型是
std::vector

还有其他有趣的用例,确实没有太多理由拒绝。

假设我们有一个

std::expected<int, std::exception_ptr>
的范围,称之为
results
。也许我们进行了大量计算,但其中一些计算失败了。我可以将其收集到
std::vector<std::expected<int, std::exception_ptr>>
中,这可能会有用。但还有另一种选择:我可以将其收集到
std::expected<std::vector<int>, std::exception_ptr>
中。也就是说,如果所有计算都成功,我将得到所有结果作为值类型。但是,如果其中任何一个失败,我都会收到第一个错误。这是一件非常有用的事情,从概念上讲,这与
ranges::to
对其输入所做的事情非常一致 - 所以这可以支持:

auto processed = results | ranges::to<std::expected>();
if (not processed) {
    std::rethrow_exception(processed.error());
}

std::vector<int> values = std::move(processed).value();
// go do more stuff

这对支持非常有用 - 特别是因为支持它并不需要任何成本。我们只是不能过早地拒绝它。


0
投票

(我是从关于标准提案邮件列表的讨论来到这里的。)

P1206 非常清楚

C
应该是 container 类型 - 例如
vector
,但绝对不是
optional
variant
string
int
或...(因此,与上面的2022年的巴里相反,我断言
ranges::to<std::expected>
不是)正如您在 中指出的,使用它需要您自担风险。邮件列表线程,
ranges::to<std::optional>
用于在 C++23 中“工作”,但 C++26 使其停止工作,请自行承担风险。)

好的,所以

ranges::to

 适合与容器一起使用。但 C++ 至少提供了三种不是范围的容器类型:
stack<T>
queue<T>
priority_queue<T>

static_assert(!std::ranges::range<std::stack<int>>); auto stk = std::views::iota(1, 10) | std::ranges::to<std::stack<int>>(); // OK
这会调用 

stack<int>::stack(from_range_t, R&&)

 从这 9 个整数创建一个堆栈。但 
stack<int>
 本身并不是范围类型;它是一个
容器,但它不是一个范围

因此,限制

ranges::to

 仅适用于 
range
 类型显然是不正确的。我们缺乏“容器”的任何标准概念。因此,唯一安全的解决方案(不会无缘无故地禁止我们想要允许的任何事情的解决方案)是让 
ranges::to
 基本上不受约束。一个例外是:因为我们
知道没有容器曾经是view
,所以我们可以安全地禁止
view
类型。哦,没有容器类型永远是标量类型:我们可以安全地要求 
C
 是类类型。除此之外,我们无能为力。

© www.soinside.com 2019 - 2024. All rights reserved.