Rozsahy C++20: Nové paradigma
Objevte řady C++20 — moderní redesign rozhraní algoritmu STL. Naučte se koncepty rozsahů, algoritmy založené na rozsahu, projekce a operátor potrubí pro transformace složených dat.
Proč Ranges?
C++20 Ranges are a fundamental upgrade to the STL's algorithm model. The traditional STL requires passing iterator pairs to every algorithm, which is verbose and error-prone (you can accidentally pass iterators from different containers). Ranges solve this by allowing you to pass the container itself — or any object that definesbegin()andend().
Key improvements over traditional algorithms:
1. Pass containers directly —std::ranges::sort(vec)instead ofstd::sort(vec.begin(), vec.end())
2. Projections — transform elements before comparing, without modifying them
3. Concepts — algorithms have clear compile-time requirements
4. Composability — chain operations with the pipe operator|
5. Lazy evaluation — views compute elements on demand, avoiding intermediate copies
Koncepty rozsahu
C++20 definuje hierarchii konceptů rozsahu v:
- std::ranges::range – mábegin()aend()
- std::ranges::sized_range – másize()v O(1)
- std::ranges::input_range — lze přečíst jednou (jako vstupní iterátory)
- std::ranges::forward_range — víceprůchodový posuv vpřed
- std::ranges::bidirectional_range — vpřed + vzad
- std::ranges::random_access_range — obousměrné + O(1) skoky
- std::ranges::contiguous_range — náhodný přístup + souvislá paměť
– std::ranges::common_range –begin()aend()vrátí stejný typ
- std::ranges::viewable_range — lze převést na pohled
Tyto koncepty se používají k omezení algoritmů založených na rozsahu, což vám poskytne jasné chybové zprávy, když projdete nekompatibilním rozsahem.
Algoritmy založené na rozsahu
Algoritmy založené na rozsahu fungují vstd::ranges::a přijímají celé kontejnery. Místo holých iterátorů také vracejí bohatší typy výsledků.
#include <algorithm>
#include <ranges>
#include <vector>
#include <string>
#include <iostream>
struct Employee {
std::string name;
int age;
double salary;
};
int main() {
std::vector<int> nums = {5, 3, 1, 4, 1, 5, 9, 2, 6};
// Pass the container directly — no begin/end needed
std::ranges::sort(nums);
// nums: {1, 1, 2, 3, 4, 5, 5, 6, 9}
// find returns a borrowed iterator (safe with temporary ranges)
auto it = std::ranges::find(nums, 4);
if (it != nums.end()) {
std::cout << "Found: " << *it << '\n';
}
// count_if with ranges
auto even_count = std::ranges::count_if(nums, [](int x) { return x % 2 == 0; });
std::cout << "Even count: " << even_count << '\n'; // 3
// min/max on entire range
auto [min_it, max_it] = std::ranges::minmax_element(nums);
std::cout << "Min: " << *min_it << ", Max: " << *max_it << '\n';
// Sorting employees by salary (descending)
std::vector<Employee> team = {
{"Alice", 30, 95000},
{"Bob", 25, 72000},
{"Charlie", 35, 110000},
};
std::ranges::sort(team, std::ranges::greater{}, &Employee::salary);
// Charlie (110k), Alice (95k), Bob (72k)
for (const auto& e : team) {
std::cout << e.name << ": $" << e.salary << '\n';
}
}Projekce — Transformujte před porovnáním
Projekce jsou jedinečnou vlastností algoritmů založených na rozsahu. Umožňují vám určit, jak „zobrazit“ každý prvek pro porovnání nebo zpracování, aniž byste museli prvek samotný upravovat.
#include <algorithm>
#include <ranges>
#include <vector>
#include <string>
#include <iostream>
#include <cctype>
int main() {
// Sort strings case-insensitively using a projection
std::vector<std::string> words = {"Banana", "apple", "Cherry", "date"};
std::ranges::sort(words, {}, [](const std::string& s) {
std::string lower = s;
std::transform(lower.begin(), lower.end(), lower.begin(),
[](char c) { return std::tolower(c); });
return lower;
});
// words: {"apple", "Banana", "Cherry", "date"}
// Original strings preserved, sorted by lowercase version
for (const auto& w : words) std::cout << w << ' ';
std::cout << '\n';
// Using member pointer as projection
struct Point { int x, y; };
std::vector<Point> points = {{3, 1}, {1, 5}, {2, 3}};
// Sort by x coordinate
std::ranges::sort(points, {}, &Point::x);
// points: {{1,5}, {2,3}, {3,1}}
// Find max by y coordinate
auto max_y = std::ranges::max_element(points, {}, &Point::y);
std::cout << "Max y: (" << max_y->x << ", " << max_y->y << ")\n"; // (1, 5)
// Binary search with projection
bool found = std::ranges::binary_search(points, 2, {}, &Point::x);
std::cout << std::boolalpha << "Has x=2: " << found << '\n'; // true
}Operátor potrubí pro kompozici
Potrubní operátor|je charakteristickým znakem řad C++20. Umožňuje vám zřetězit adaptéry rozsahu (pohledy) do potrubí, které se čte zleva doprava, jako unixové shell potrubí:
auto result = numbers
| std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; })
| std::views::take(5);This reads naturally: "take numbers, keep the even ones, square them, take the first 5." The pipeline is lazy — no computation happens until you iterate over výsledek. To znamená, že nejsou přiděleny žádné mezilehlé vektory.
Pohledy do hloubky prozkoumáme v další lekci.
Algoritmy rozsahu chrání před visícími iterátory z dočasných rozsahů. Pokud minete dočasný kontejner, vrácený iterátor bude viset, protože dočasný je zničen u středníku:
auto it = std::ranges::find(get_vector(), 42);
// Returns std::ranges::dangling instead of an iterator!
// Attempting to dereference it won't compile.This is a compile-time safety feature — traditional std::find would silently return a dangling iterator, leading to undefined behavior at runtime. The std::rozsahy::dangling typ sentinelu způsobuje chybu kompilace.
Preferujte v novém kódu algoritmystd::ranges::– jsou bezpečnější (žádné neodpovídající iterátory, visící ochrana), čitelnější (předávají kontejnery přímo) a výkonnější (projekce, koncepty).
Používejte tradiční algoritmystd::, když:
- Vaše kódová základna se zaměřuje na kompilátory starší než C++20
- Potřebujete specifické přetížení, které rozsahy neposkytují
- Pracujete s dvojicemi iterátorů ze starších rozhraní API
Poznámka: Algoritmystd::ranges::jsou ne náhrady typu drop-in. Některé vrátí různé typy (např.std::ranges::sortvrátí koncový iterátor seřazeného rozsahu). Při migraci zkontrolujte dokumentaci.
- Řady C++20 vám umožňují předat kontejnery přímo algoritmům – už žádné standardní
begin()/end() - Projekce transformují prvky pro porovnání bez úpravy původních dat
- Koncepty rozsahů (
input_range,random_access_rangeatd.) poskytují jasná omezení v době kompilace - Operátor potrubí
|umožňuje skládatelná potrubí zpracování dat zleva doprava std::ranges::danglingzabraňuje visícím chybám iterátoru v době kompilace- Preferujte algoritmy
std::ranges::v novém kódu C++20 pro bezpečnost, čitelnost a výkon
Kvíz — Otestujte své znalosti
(20 XP)1. Co je projekce v kontextu algoritmů rozsahu C++20?
2. Co vrátí`std::ranges::find`při volání s dočasným kontejnerem?
3. Co dělá`std::ranges::sort(vec, std::ranges::greater{}, &Employee::salary)`?