考虑How do I write a range pipeline that uses temporary containers?。问题是如何构建一个视图,使用某个给定的函数
转换每个元素T
std::vector<T> f(T t);
同时遵守the restriction(从那里的最佳答案借用)
视图是一个轻量级的包装器,它以某种自定义方式呈现基础元素序列的视图,而不会发生变异或复制。视图创建和复制起来很便宜,并且具有非拥有的引用语义。
基本上,所有答案似乎都同意,由于这种限制,不能通过视图来完成。
我不明白这是如何适应支持partial_sum
的图书馆。
考虑以下美化整数:
#include <vector>
#include <iostream>
#include <memory>
#include <range/v3/all.hpp>
using namespace ranges;
struct glorified_int {
explicit glorified_int(int i) : m_i{std::make_shared<int>(i)} {}
operator int() const { return *m_i; }
std::shared_ptr<int> m_i;
};
glorified_int operator+(const glorified_int &lhs, const glorified_int &rhs) {
glorified_int ret{(int)lhs + (int)rhs};
return ret;
}
它基本上只是将int
包含在一个将std::shared_ptr
存储在一个类中,允许初始化,提取和添加。 W.r.t.非拥有引用语义,我看不出它与容器之间的根本区别,如std::vector
。
范围似乎没有问题将partial_sum
应用于此,但是:
int main() {
std::vector<glorified_int> vi{ glorified_int{1}, glorified_int{2} };
for(const auto &ps: vi | view::partial_sum())
std::cout << ps << std::endl;
打印
$ ./a.out
1
3
不是(美化的整数)3暂时在这里吗?它肯定不是原始序列的一部分。此外,部分和是显着的有状态转换,所以范围如何保证
创建和复制视图很便宜,并且具有非拥有的引用语义。
视图与累积对象一样昂贵。
请注意,进一步链接这一点也没有问题(即,它不是一个动作):
vi | view::partial_sum() | view::take(10);
那有什么不同呢?
完整代码
#include <vector>
#include <iostream>
#include <memory>
#include <range/v3/all.hpp>
using namespace ranges;
struct glorified_int {
explicit glorified_int(int i) : m_i{std::make_shared<int>(i)} {}
operator int() const { return *m_i; }
std::shared_ptr<int> m_i;
};
glorified_int operator+(const glorified_int &lhs, const glorified_int &rhs) {
glorified_int ret{(int)lhs + (int)rhs};
return ret;
}
int main() {
std::vector<glorified_int> vi{ glorified_int{1}, glorified_int{2} };
for(const auto &ps: vi | view::partial_sum())
std::cout << ps << std::endl;
vi | view::partial_sum() | view::take(10);
}
答案 0 :(得分:4)
视图的观点是它不会占用或要求输入范围的任何元素的所有权,复制或修改。但是,要求一个观点不需要任何状态。即使take()
或filter()
也有某种状态(分别是计数器和谓词)。
在这种特定情况下,partial_sum
不必拥有输入范围的任何元素。这是输入范围的工作。它也不需要复制或修改它们。它只需要跟踪自己的状态 - 运行总和(optional<glorified_int>
)和进行求和的二元函数(plus
)。它拥有自己的一个对象,但该对象完全存在于输入范围之外。这仍然是一个观点,只是一个有状态的观点。
你写道:
视图与累积对象一样昂贵。
这是事实。但是,许多观点也是如此。 transform()
和我们用来转换视图的函数一样昂贵,也许你有一个庞大的有状态,昂贵,内存分配的怪物。
当Eric写关于创建和复制的廉价时,我相信他的意思是创建和复制整个输入范围以产生新的范围。虽然partial_sum()
需要保留运行金额,但在您的情况下,由于该元素需要分配而且不便宜,这仍然比编写基于操作的partial_sum
便宜得多:< / p>
// cheap version
for(const auto &ps: vi | view::partial_sum()) { ... }
// expensive version
std::vector<glorified_int> partial_sums;
if (!vi.empty()) {
auto it = vi.begin();
partial_sums.emplace_back(*it++);
for (; it != vi.end(); ++it) {
partial_sums.emplace_back(*it + partial_sums.back());
}
}
for (const auto &ps : partial_sums) { ... }
我们显然不需要整个partial_sums
向量来做我们想要的事情(如果我们确实需要它,那么,没办法)。该视图为我们提供了一种廉价的方法来查看部分金额。