range-v3禁止在临时容器上使用视图,以帮助我们避免创建悬空迭代器。您的示例恰好说明了为什么在视图组合中需要此规则:
auto rng = src | view::transform(f) | view::join
如果
view::join
存储由
f
返回的临时向量的
begin
和
end
迭代器,那么它们将在使用之前失效。
“这很好,Casey,但为什么range-v3视图不像这样内部存储临时范围?”
因为性能。就像STL算法的性能取决于迭代器操作为O(1)的要求一样,视图组合的性能取决于视图操作为O(1)的要求。如果视图在你背后将临时范围存储在内部容器中,则视图操作的复杂性 - 因此组合的复杂性 - 将变得不可预测。
“好吧,既然我理解了所有这些精美的设计,我该怎么让它工作?!?”
由于视图组合不会为您存储临时范围,因此您需要自己将其转储到某种存储中,例如:
#include <iostream>
#include <vector>
#include <range/v3/range_for.hpp>
#include <range/v3/utility/functional.hpp>
#include <range/v3/view/iota.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/transform.hpp>
using T = int;
std::vector<T> f(T t) { return std::vector<T>(2, t); }
int main() {
std::vector<T> buffer;
auto store = [&buffer](std::vector<T> data) -> std::vector<T>& {
return buffer = std::move(data);
};
auto rng = ranges::view::ints
| ranges::view::transform(ranges::compose(store, f))
| ranges::view::join;
unsigned count = 0;
RANGES_FOR(auto&& i, rng) {
if (count) std::cout << ' ';
else std::cout << '\n';
count = (count + 1) % 8;
std::cout << i << ',';
}
}
请注意,这种方法的正确性取决于
view::join
是一个输入范围,因此是单遍的。
“这不是新手友好的。哎呀,甚至不是专家友好的。为什么 range-v3 中没有对‘临时存储物化™’提供支持?”
因为我们还没有开始处理它 - 欢迎提交补丁 ;)
for (int i : std::vector({1,2,3}) | mytransform([](int i) { return i+1; })) { std::cout << i << std::endl; }
。 我的mytransform函数会因为std::vector
过早被删除(基于范围的for循环的右手边的临时变量的生命周期会延长到循环结束,但其他任何临时变量都不会被延长)而产生垃圾或崩溃。@bradgonesurfing上面的评论解决了这个问题。 - Don Hatch