std::ranges 不能接受临时变量谬误
std::ranges的临时变量谬误
起因
经常在很多cpp std::ranges的教程中说不能使用临时序列:
auto seq = std::vector<int>{1, 2, 3} | std::ranges::views::filter([](int x){return x%2 == 1;});
纠正
上面那种写法是完全正确的,因为很多view都有推导指引,将模板参数中的V推导为std::views::all_t.
template< class R, class Pred >
filter_view( R&&, Pred ) -> filter_view<views::all_t<R>, Pred>;
而std::views::all_t又会根据参数的值类别来生成不同的view,比如最开头代码是个右值就会生成std::ranges::owning_view。这个owning_view会移动右值并拥有数据。
真正应该防范的是这类代码:
auto create_numbers = []() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
return numbers | std::views::filter([](int n){ return n % 2 == 0; }) | std::ranges::to<std::vector<int>>();
};
auto even_numbers = create_numbers();
for (int n : even_numbers) { // 安全访问
std::cout << n << " ";
}
numbers是左值,所以std::views::all返回std::ranges::ref_view保存的是一个引用。当函数返回时,就变成了悬垂引用。
再补充一个
很多view都有一个non-propagating-cache成员,比如std::ranges::filter_view。这个成员会缓存一个迭代器。
所以下面这段代码会运行错误:
std::vector<int> vec{1, 2, 3};
auto seq = vec | std::ranges::views::filter([](int x) { return x % 2 == 1; });
for (auto &i : seq)
{
i *= 2;
std::println("{}", i);
} // 2 6
for (auto i : seq)
{
std::println("{}", i);
}//2
明明我们已经把vec中所有的元素都变为偶数了啊,vec = {2, 2, 6}。按理说第二次循环应该什么都不输出,但filter_view缓存了第一次的iterator,即&vec[0]。
还有下面这种情况更是直接会越界访问:
for (auto i: seq)
{
std::println("{}", i);
}
vec.push_back(4);
for (auto i: seq)
{
std::println("{}", i);
}
所以在使用ranges时,最好不要在求值之前更改数据源。
浙公网安备 33010602011771号