为什么范围不能用于管道库功能?

ein*_*ica 14 c++ c++14 range-v3 std-ranges

乔纳森博卡拉(作者流利C ++)写了一个叫做库的管道

这个“管道”,存储库的主页说,不像范围的使用,即使它看起来一样:它不是基于懒惰的拉动,而是基于急切的推动。但是据说不能使用范围库来执行各种“管道”操作。例如:

  • unzip - 采用压缩输入 - 本质上是一系列 k 元组 - 并产生 k 个单独的、独立的输出。
  • fork - 生成容器/范围的多个(独立)副本。

我不太明白为什么原则上是这样。(当然,除了无法获得结束迭代器/哨兵的范围。)

Nic*_*las 11

正在讨论的本质上是基于推送的处理方法和基于拉的处理方法之间的区别。在像这个管道库这样的推送系统中,您建立了一个处理链,每个处理步骤都将其数据直接推送到下一个处理步骤。在像范围这样的拉式系统中,您可以建立数据的表示,您可以根据需要访问和修改它。处理不会自行发生;只有当有人试图消耗范围时才会发生这种情况。

unzipfork操作都是一个一对多的操作:他们把一个单一的输入,并将其映射到多个处理操作。

作为一个推送系统,管道库由于其 API 的结构可以处理一对多的操作。一个操作由一个函数调用表示;输入由使用点暗示(使用>>=或传递给处理器)。函数的参数定义了它的输出(忽略处理器本身的参数)。由于 C++ 函数可以有任意数量的参数,一对多的映射操作自然会失败。您只需为各种输出提供合适的处理器。

作为拉式系统,范围基于返回值。C++ 没有返回多个值的语言机制,所以我们能做的最好的事情就是返回一个代表多个值的“值”。

然而,范围适配器链接最终是基于输入的范围。而“代表多个值的‘值’”本身并不是一个范围。它可能包含范围,但这并不使它成为范围。

因此,现在您必须采用这种绝对“非范围”的类型,并使您的所有范围适配器都能使用它。应用范围适配器必须在整个类型中广播该操作,从而创建多对多操作。做到这一点并不容易。

但更重要的是......这可能不是你想要的。如果您fork是一个范围,那么您几乎肯定希望对复制的范围进行不同的处理。这完全关闭了使用该|操作来做到这一点的任何机会。您必须构建将适配器应用于这些范围元组的特定部分的方法。这些方式越来越像基于推送的处理器。

归根结底,拉式系统在每个级别只有一个输出。这只是此类 API 核心概念的一部分:每个处理步骤都会生成一个范围。这有其优点(延迟处理),但表示一对多操作是其弱点之一。

范围当然可以有一个unzip功能(fork实际上只是复制范围)。但它不会是一个|样式适配器;这将是一个函数,它接受某个可分解类型的范围并返回一个范围元组。如果您想对它们进行更多处理,则需要将元组存储在一个值中,访问各个元素,并根据需要使用它们。