C++的范围库(std::ranges)添加于C++20,范围库是一个对迭代器和泛型算法库的扩展,使得迭代器和算法可以通过组合变得更强大,并且减少错误。
[begin, end)
迭代器对,例如可以从容器隐式获得范围。所有接受迭代器对的算法现在都有接受范围的重载(例如 ranges::sort)
[start, size)
有长度的序列,例如 views::counted 的结果
[start, predicate)
条件终止序列,例如 views::take_while 的结果
[start..)
未知边界序列,例如 views::iota 的结果
摘取自👉cppreference.com
受约束算法
👉受约束算法
示例
下述的示例用 lambda 表达式递增 vector 所有的元素,然后用函数对象中重载的 operator() 计算它们的和。注意,要计算总和的话,建议使用专用的算法 std::accumulate 。
例子:
#include <algorithm>
#include <cassert>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
struct Sum {
void operator()(int n) { sum += n; }
int sum{0};
};
int main() {
std::vector<int> nums{3, 4, 2, 8, 15, 267};
auto print = [](const auto& n) { std::cout << ' ' << n; };
namespace ranges = std::ranges;
std::cout << "before:";
ranges::for_each(std::as_const(nums), print);
print('\n');
ranges::for_each(nums, [](int& n){ ++n; });
// 对每个数调用 Sum::operator()
auto [i, s] = ranges::for_each(nums.begin(), nums.end(), Sum());
assert(i == nums.end());
std::cout << "after: ";
ranges::for_each(nums.cbegin(), nums.cend(), print);
std::cout << "\n" "sum: " << s.sum << '\n';
using pair = std::pair<int, std::string>;
std::vector<pair> pairs{{1,"one"}, {2,"two"}, {3,"three"}};
std::cout << "project the pair::first: ";
ranges::for_each(pairs, print, [](const pair& p) { return p.first; });
std::cout << "\n" "project the pair::second:";
ranges::for_each(pairs, print, &pair::second);
print('\n');
}
结果:
before: 3 4 2 8 15 267
after: 4 5 3 9 16 268
sum: 305
project the pair::first: 1 2 3
project the pair::second: one two tree
示例
以下代码用 fill() 设置 int 的 vector 的所有元素为 -1 :
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector< int > v { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
namespace ranges = std::ranges;
ranges::fill(v.begin(), v.end(), -1);
for (auto elem : v) {
std::cout << elem << " ";
}
std::cout << "\n";
ranges::fill(v, 10);
for (auto elem : v) {
std::cout << elem << " ";
}
std::cout << "\n";
}
结果:
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1
10 10 10 10 10 10 10 10 10 10
迭代器库的扩展
范围访问
定义于头文件 <ranges>
定义于头文件 <iterator>
ranges::begin(C++20) | 返回指向范围起始的迭代器 (定制点对象) |
---|---|
ranges::end(C++20) | 返回指示范围结尾的哨位 (定制点对象) |
ranges::cbegin(C++20) | 返回指向只读范围起始的迭代器 (定制点对象) |
ranges::cend(C++20) | 返回指示只读范围结尾的哨位 (定制点对象) |
ranges::rbegin(C++20) | 返回指向范围的逆向迭代器 (定制点对象) |
ranges::rend(C++20) | 返回指向范围的逆向尾迭代器 (定制点对象) |
ranges::crbegin(C++20) | 返回指向只读范围的逆向迭代器 (定制点对象) |
ranges::crend(C++20) | 返回指向只读范围的逆向尾迭代器 (定制点对象) |
ranges::size(C++20) | 返回等于范围大小的整数 (定制点对象) |
ranges::ssize(C++20) | 返回等于范围大小的有符号整数 (定制点对象) |
ranges::empty(C++20) | 检查范围是否为空 (定制点对象) |
ranges::data(C++20) | 获得指向连续范围的起始的指针 (定制点对象) |
ranges::cdata(C++20) | 获得指向只读连续范围的起始的指针 (定制点对象) |
例子:
#include <iostream>
#include <vector>
#include <ranges>
int main() {
std::vector<int> v = { 3, 1, 4 };
auto vi = std::ranges::begin(v);
std::cout << *vi << '\n';
*vi = 42; // OK
int a[] = { -5, 10, 15 };
auto ai = std::ranges::begin(a);
std::cout << *ai << '\n';
*ai = 42; // OK
}
3
-5
#include <cstring>
#include <iostream>
#include <ranges>
#include <string>
int main() {
std::string s {"Hello world!\n"};
char a[20]; // C 风格字符串的存储
std::strcpy(a, std::ranges::data(s));
// [data(s), data(s) + size(s)] 保证为 NTBS
std::cout << a;
}
结果:
Hello world!
#include <ranges>
#include <iostream>
int main() {
auto const ints = {0,1,2,3,4,5};
auto even = [](int i) { return 0 == i % 2; };
auto square = [](int i) { return i * i; };
// 组合视图的“管道”语法:
for (int i : ints | std::views::filter(even) | std::views::transform(square)) {
std::cout << i << ' ';
}
std::cout << '\n';
// 传统的“函数式”组合语法:
for (int i : std::views::transform(std::views::filter(ints, even), square)) {
std::cout << i << ' ';
}
}
结果:
0 4 16