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.

就比如std::ranges::filter_view:

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时,最好不要在求值之前更改数据源。

posted @ 2026-09-12 01:29  杨忆州  阅读(14)  评论(0)    收藏  举报