← SEARCHABLE ARCHIVE
C / C++ · 2026-08-12

从 C++ 基础到掌握 — 第 18 集

使用 Ranges 和 Views 构建惰性数据管线

INTERACTIVE READING MODE →

本博客文章以教学为目的编写。 内容可能存在错误,请结合目标编译器和标准库文档进行确认。

迭代器把容器和算法分离,但传统调用需要反复传入 begin 和 end。C++20 Ranges 可直接接收范围,Views 则能组合不复制数据的惰性变换。

直接传入范围

std::vector<int> scores{30, 80, 50, 100};
std::ranges::sort(scores);

auto found = std::ranges::find(scores, 80);
if (found != scores.end())
{
    // 找到了值。
}

不必分别书写 begin 和 end,能降低混用不同容器迭代器的风险。projection 还能直接指定成员。

std::ranges::sort(players, {}, &Player::Score);

View 在需要时才计算

auto visibleNames = players
    | std::views::filter([](const Player& p) { return p.IsVisible; })
    | std::views::transform([](const Player& p) -> const std::string&
      {
          return p.Name;
      })
    | std::views::take(10);

for (const std::string& name : visibleNames)
    DrawName(name);

这个管线不会创建三个中间 vector。迭代请求元素时才执行 filter 和 transform,take(10) 最多处理十项。

大多数 View 不拥有源数据

auto MakeBadView()
{
    std::vector<int> local{1, 2, 3};
    return local | std::views::filter([](int value) { return value > 1; });
}

返回的 View 引用局部 vector,但函数返回后 vector 已销毁。之后迭代会访问失效对象。

保存或返回 View 前,必须证明源 range 的生命周期覆盖所有使用。如果结果需要独立拥有数据,应明确收集到容器中。

惰性求值还会把错误和副作用推迟到迭代时。修改源容器仍可能使迭代器失效。

Ranges 的价值在于用组合描述数据流,而不只是缩短语法。管线过长或混入状态修改时,应命名中间步骤或改用明确的算法和循环。