Jonathan Boccara(Fluent C++的作者)编写了一个名为管道的库。
这个“管道”,存储库的主页说,不像使用范围,即使它看起来一样:它不是基于延迟拉动,而是急切推动。但是据说不能使用范围库来执行各种“管道”操作。例如:
- unzip - 采用压缩输入 - 本质上是一系列 k 元组 - 并产生 k 个单独的、独立的输出。
- fork - 生成容器/范围的多个(独立)副本。
我不太明白为什么原则上会出现这种情况。(当然,您无法获得结束迭代器/哨兵的范围除外。)
Jonathan Boccara(Fluent C++的作者)编写了一个名为管道的库。
这个“管道”,存储库的主页说,不像使用范围,即使它看起来一样:它不是基于延迟拉动,而是急切推动。但是据说不能使用范围库来执行各种“管道”操作。例如:
我不太明白为什么原则上会出现这种情况。(当然,您无法获得结束迭代器/哨兵的范围除外。)
正在讨论的本质上是基于推送的处理方法与基于拉取的处理方法之间的区别。在像这个管道库这样的推送系统中,您建立了一个处理链,每个处理步骤都将其数据直接推送到下一个处理步骤。在像范围这样的拉取系统中,您可以建立数据的表示形式,您可以根据需要访问和修改它。处理不会自行发生;仅当有人尝试使用该范围时才会发生这种情况。
unzip
和fork
操作都是一对多的操作:它们接受一个输入并将其映射到许多处理操作。
作为一个推送系统,管道库可以处理一对多的操作,因为它的 API 结构。一个操作由一个函数调用表示;输入由使用点暗示(使用>>=
或传递给处理器)。函数的参数定义了它的输出(忽略处理器本身的参数)。而且由于 C++ 函数可以有任意数量的参数,一对多的映射操作自然会失败。您只需为各种输出提供适当的处理器。
作为拉式系统,范围基于返回值。C++没有返回多个值的语言机制,所以我们能做的最好的就是返回一个代表多个值的“值”。
但是,范围适配器链接最终基于范围的输入。而一个“代表多个值的‘值’”本身并不是一个范围。它可能包含范围,但这并不意味着它是一个范围。
所以现在你必须采用这种非常明确的“不是范围”类型,并让你所有的范围适配器都可以使用它。应用范围适配器必须跨类型广播该操作,创建多对多操作。做到这一点并不容易。
但更重要的是……这可能不是你想要的。如果您fork
是一个范围,那么您几乎肯定希望对复制的范围进行不同的处理。这完全关闭了使用该|
操作执行此操作的任何机会。您必须构建将适配器应用于这些范围元组的特定部分的方法。这些方式越来越像基于推送的处理器。
归根结底,拉式系统在每个级别只有一个输出。这只是此类 API 核心概念的一部分:每个处理步骤都会生成一个范围。这有其优势(延迟处理),但表示一对多操作是其薄弱环节之一。
范围当然可以有一个unzip
功能(fork
实际上只是复制范围)。但它不会是|
样式适配器;这将是一个函数,它接受某个可分解类型的范围并返回一个范围元组。如果您想对它们进行更多处理,那么您需要将元组存储在一个值中,访问各个元素,并在您认为合适的时候使用它们。