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

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

本文关键字:管道 语法 C++20 范围 为什么      更新时间:2023-10-16

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

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

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

不起作用,您必须以不自然的方式编写它:

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

详细信息可以在这里和这里找到,但它归结为这样一个事实,即累积不能消除 2 个不同用例之间的歧义。

所以这让我想到:

如果 C++20 要求你必须使用管道来使用范围,也就是你不能写

vector<int> v;
sort(v);

但你必须写

vector<int> v
v|sort();

这会解决模棱两可的问题吗?

如果是这样,尽管对于使用std::sort和其他STL算法的人来说是不自然的,但我想知道从长远来看,这是否是一个更好的设计选择。

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

您需要区分范围算法和范围适配器。算法是对一系列值执行泛型操作的函数。适配器是创建范围视图以修改范围表示的函数。适配器由|操作员链接;算法只是常规函数。

有时,同一概念事物可以具有算法和适配器形式。transform既是算法又是适配器。前者将转换存储为输出范围;后者创建输入的视图范围,根据请求延迟计算转换。

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

另请注意,C++20 中没有sort适配器。排序适配器必须创建一个视图范围,该视图范围以某种方式混合在源区域中的元素周围。它必须为新的值序列分配存储(即使它只是对值的迭代器/指针/索引进行排序(。并且分类必须在施工时完成,因此不会发生懒惰的操作。

这也是为什么accumulate不是这样工作的。这不是"模棱两可"的问题;这是操作的基本性质问题。累积从范围计算值;它不会从现有范围计算新范围。这是算法的工作,而不是适配器。

某些任务在算法形式中很有用。有些任务在适配器形式中很有用(您发现很少有类似zip算法(。有些任务在两者中都很有用。但是,由于这是用于不同目的的两个独立概念,因此它们具有不同的调用方式。

这会解决歧义问题吗?

是的。

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

从这个意义上说,没有歧义。

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

auto sum = accumulate("hello"s);

它实际上并没有对该字符串中的char求和,实际上是等待范围累积的占位符"hello"s初始值。