C++20中的`<ranges>`库详解

1,023 阅读5分钟

C++20中的<ranges>库详解

C++20引入了范围库(Ranges),这是一个功能强大且灵活的库,用于处理和操作集合。范围库是对迭代器和算法库的扩展,提供了一种更直观、更易用的集合操作方式。本文将详细介绍C++20中的<ranges>库及其主要特性和用法。

范围的基本概念

范围(Range)表示一个可以遍历的序列,范围库中的主要组件包括:

  1. 范围概念(Range Concepts):定义了范围和迭代器的特性。
  2. 视图(Views):懒惰计算的范围转换器。
  3. 适配器(Adapters):用于组合和修改范围的工具。
  4. 范围算法(Range Algorithms):对范围进行操作的算法。

范围概念

范围库中的概念定义在std::ranges命名空间中,主要的范围概念包括:

  • std::ranges::range:表示一个范围。
  • std::ranges::input_range:表示一个输入范围。
  • std::ranges::output_range:表示一个输出范围。
  • std::ranges::forward_range:表示一个前向范围。
  • std::ranges::bidirectional_range:表示一个双向范围。
  • std::ranges::random_access_range:表示一个随机访问范围。
  • std::ranges::contiguous_range:表示一个连续范围。

范围视图

视图(View)是范围库中的核心组件之一,提供了一种懒惰计算的方式来操作范围。视图不会立即计算结果,而是当结果被访问时才进行计算。

1. std::views::all

std::views::all用于将一个范围转换为视图。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto view = std::views::all(vec);
    
    for (int v : view) {
        std::cout << v << " ";
    }
    // 输出:1 2 3 4 5
    return 0;
}

2. std::views::filter

std::views::filter用于过滤范围中的元素。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto even_view = vec | std::views::filter([](int n) { return n % 2 == 0; });

    for (int v : even_view) {
        std::cout << v << " ";
    }
    // 输出:2 4
    return 0;
}

3. std::views::transform

std::views::transform用于对范围中的元素进行转换。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto square_view = vec | std::views::transform([](int n) { return n * n; });

    for (int v : square_view) {
        std::cout << v << " ";
    }
    // 输出:1 4 9 16 25
    return 0;
}

4. 组合视图

视图可以组合使用,实现复杂的范围操作。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5, 6};
    auto even_square_view = vec 
                          | std::views::filter([](int n) { return n % 2 == 0; })
                          | std::views::transform([](int n) { return n * n; });

    for (int v : even_square_view) {
        std::cout << v << " ";
    }
    // 输出:4 16 36
    return 0;
}

范围算法

范围库中的算法是标准算法的扩展,支持直接对范围进行操作,而无需显式地处理迭代器。

1. std::ranges::for_each

std::ranges::for_each用于对范围中的每个元素执行操作。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::ranges::for_each(vec, [](int& n) { n *= 2; });

    for (int v : vec) {
        std::cout << v << " ";
    }
    // 输出:2 4 6 8 10
    return 0;
}

2. std::ranges::sort

std::ranges::sort用于对范围进行排序。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {5, 3, 1, 4, 2};
    std::ranges::sort(vec);

    for (int v : vec) {
        std::cout << v << " ";
    }
    // 输出:1 2 3 4 5
    return 0;
}

3. std::ranges::copy

std::ranges::copy用于将一个范围的内容复制到另一个范围。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> src = {1, 2, 3, 4, 5};
    std::vector<int> dest(5);
    std::ranges::copy(src, dest.begin());

    for (int v : dest) {
        std::cout << v << " ";
    }
    // 输出:1 2 3 4 5
    return 0;
}

4. 其他算法

范围库还包括其他常用的算法,如std::ranges::findstd::ranges::countstd::ranges::unique等。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    auto it = std::ranges::find(vec, 3);
    if (it != vec.end()) {
        std::cout << "Found: " << *it << std::endl; // 输出:Found: 3
    }

    int count = std::ranges::count(vec, 2);
    std::cout << "Count of 2: " << count << std::endl; // 输出:Count of 2: 1

    return 0;
}

范围适配器

范围适配器用于组合和修改范围,提供了一种直观的方式来构建复杂的范围操作。

1. std::views::reverse

std::views::reverse用于逆转范围的元素顺序。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto reversed_view = std::views::reverse(vec);

    for (int v : reversed_view) {
        std::cout << v << " ";
    }
    // 输出:5 4 3 2 1
    return 0;
}

2. std::views::takestd::views::drop

std::views::take用于获取范围的前N个元素,std::views::drop用于跳过范围的前N个元素。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto first_three = vec | std::views::take(3);
    auto last_two = vec | std::views::drop(3);

    for (int v : first_three) {
        std::cout << v << " ";
    }
    // 输出:1 2 3
    std::cout << std::endl;

    for (int v : last_two) {
        std::cout << v << " ";
    }
    // 输出:4 5
    return 0;
}

3. std::views::zipstd::views::zip_with

std::views::zip用于将多个范围的元素合并为一个范围,std::views::zip_with用于将多个范围的元素合并并应用一个操作。

#include <ranges>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec1 = {1, 2, 3};
    std::vector<int> vec2 = {4, 5, 6};
    auto zipped_view = std::views::zip(vec1, vec2);

    for (const auto& [v1, v2]

 : zipped_view) {
        std::cout << "(" << v1 << ", " << v2 << ") ";
    }
    // 输出:(1, 4) (2, 5) (3, 6)
    std::cout << std::endl;

    auto sum_view = std::views::zip_with(std::plus<>(), vec1, vec2);
    for (int sum : sum_view) {
        std::cout << sum << " ";
    }
    // 输出:5 7 9
    return 0;
}

总结

C++20中的<ranges>库提供了一种强大且灵活的方式来处理和操作集合。通过使用范围、视图、适配器和算法,开发者可以编写出更加简洁、直观和高效的代码。希望本文对<ranges>库的详细介绍能够帮助读者更好地理解和应用这一新特性,从而提升代码的质量和可维护性。