为什么 C++20 范围不只提供管道语法?

Why do C++20 ranges not provide only pipe syntax?

我知道这个问题听起来很奇怪,所以这里有一些上下文。

最近我很失望地得知 C++20 范围内的 map reduce 并不像人们预期的那样工作,即

const double val = data | transform(...) | accumulate (...);

不行,你必须这样写不自然

const double val = accumulate(data | transform(...));

可以找到详细信息here and ,但归结为 accumulate 不能消除 2 个不同用例之间的歧义。

所以这让我想到:

如果 C++20 要求您必须使用管道来使用范围,也就是您可以

vector<int> v;
sort(v);

但是你必须写

vector<int> v
v|sort();

这会解决歧义问题吗?

如果是的话,虽然对于使用 std::sort 和其他 STL 算法的人来说不自然,但我想知道在较长的 运行 中,这是否是一个更好的设计选择。

注意: 如果这个问题太模糊,请随意投票关闭,但我觉得这是一个合理的设计问题,可以以相对公正的方式回答,特别是如果我对问题的理解是错误的。

would that would solve problem of ambiguity?

是的。

如果只有一种写作方式,那么这种方式一定是唯一可能的解释。如果算法 "call" 只能是对算法的部分调用,该算法必须通过左侧范围的 | 操作完成,那么您甚至永远不会有这样的问题算法调用是部分或全部。只是总是片面的。

在这个意义上没有歧义。

但是如果你走那条路,你最终会得到这样的结果:

auto sum = accumulate("hello"s);

实际上并没有对该字符串中的 char 求和,实际上是占位符,它正在等待一个范围以初始值 "hello"s.

累加

您需要区分范围算法和范围适配器。算法是对一系列值执行通用操作的函数。适配器是创建范围视图的函数,这些视图修改范围的表示。适配器由 | 运算符链接;算法只是常规函数。

有时候,同一个概念上的东西可以有算法和适配器形式。 transform 作为算法和适配器存在。前者将转换存储到输出范围中;后者创建输入的视图范围,根据请求延迟计算转换。

这些是针对不同需求和用途的不同任务。

此外,请注意 C++20 中没有 sort 适配器 。排序适配器必须创建一个视图范围,以某种方式混合源范围中的元素。它必须为新的值序列分配存储空间(即使它只是对值进行 iterators/pointers/indices 排序)。而且排序必须在构建时完成,所以不会发生惰性操作。

这也是为什么 accumulate 不能那样工作的原因。不是"ambiguity"的问题;这是操作的基本性质的问题。累加从一个范围计算一个值;它不会根据现有范围计算新范围。那是算法的工作,而不是适配器。

有些任务在算法形式上很有用。一些任务在适配器形式中很有用(你发现很少有 zip-like 算法)。有些任务对两者都有用。但是因为这是两个用于不同目的的独立概念,所以它们有不同的调用方式。