<ranges>
Na vysoké úrovni je rozsah něco, co můžete iterovat. Oblast je reprezentována iterátorem, který označuje začátek oblasti a sentinel, který označuje konec oblasti. Sentinel může být stejný typ jako počáteční iterátor nebo se může lišit. Kontejnery, například vector
a list
, ve standardní knihovně C++ jsou rozsahy. Rozsah abstrahuje iterátory způsobem, který zjednodušuje a zesiluje vaši schopnost používat standardní knihovnu šablon (STL).
Algoritmy STL obvykle přebírají iterátory, které odkazují na část kolekce, se kterou by měly pracovat. Představte si například, jak seřadíte vector
pomocí std::sort()
. Projdete dvěma iterátory, které označují začátek a konec vector
. To poskytuje flexibilitu, ale předání iterátorů algoritmu je navíc práce, protože pravděpodobně chcete jen seřadit celou věc.
S rozsahy můžete volat std::ranges::sort(myVector);
, což je považováno za volání std::sort(myVector.begin(), myVector.end());
. V knihovnách rozsahů algoritmy přebírají rozsahy jako parametry (i když mohou v případě potřeby také přijímat iterátory). Můžou pracovat přímo s kolekcemi. Příklady algoritmů rozsahu dostupných v <algorithm>
zahrnutí copy
, , copy_n
, copy_if
, all_of
, any_of
, none_of
, find
, , find_if
, count_if
for_each
count
for_each_n
find_if_not
equal
a .mismatch
Ale možná nejdůležitější výhodou rozsahů je, že můžete vytvářet algoritmy STL, které pracují s rozsahy ve stylu, který je motivem funkčního programování.
Příklad rozsahů
Pokud chcete před rozsahy transformovat prvky kolekce, které splňovaly určité kritérium, museli jste zavést mezikrok pro uložení výsledků mezi operacemi. Pokud byste například chtěli vytvořit vektor čtverců z prvků v jiném vektoru, který je dělitelný třemi, můžete napsat něco takového:
std::vector<int> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
std::vector<int> intermediate, output;
std::copy_if(input.begin(), input.end(), std::back_inserter(intermediate), [](const int i) { return i%3 == 0; });
std::transform(intermediate.begin(), intermediate.end(), std::back_inserter(output), [](const int i) {return i*i; });
S rozsahy můžete dosáhnout stejné věci, aniž byste potřebovali intermediate
vektor:
// requires /std:c++20
std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto output = input
| std::views::filter([](const int n) {return n % 3 == 0; })
| std::views::transform([](const int n) {return n * n; });
Kromě snadnějšího čtení se tento kód vyhne přidělení paměti potřebné pro intermediate
vektor a jeho obsah. Umožňuje také vytvořit dvě operace.
V předchozím kódu se každý prvek dělitelný třemi prvky zkombinuje s operací na druhou mocninu daného prvku. Symbol svislé roury|
zřetědí operace dohromady a čte se zleva doprava.
Výsledek je output
sám o sobě druh rozsahu označovaného jako zobrazení.
Zobrazení
Zobrazení je jednoduchý rozsah. Zobrazit operace, jako je výchozí konstrukce, přesunutí konstrukce/přiřazení, kopírování konstrukce/přiřazení (pokud je k dispozici), zničení, začátek a konec --vše probíhá v konstantním čase bez ohledu na počet prvků v zobrazení.
Zobrazení jsou vytvořena adaptéry rozsahu, které jsou popsány v následující části. Další informace o třídách, které implementují různá zobrazení, naleznete v tématu Zobrazit třídy.
Způsob zobrazení prvků závisí na adaptéru rozsahu, který použijete k vytvoření zobrazení. V předchozím příkladu adaptér rozsahu vezme rozsah a vrátí zobrazení prvků dělitelných třemi. Základní oblast se nezmění.
Zobrazení jsou kompozibilní, což je výkonné. V předchozím příkladu se zobrazení vektorových prvků, které jsou dělitelné třemi, zkombinuje s zobrazením, které tyto prvky čtverce.
Prvky zobrazení se vyhodnocují laziálně. To znamená, že transformace, které použijete pro každý prvek v zobrazení, se nevyhodnocují, dokud se nezobrazí dotaz na prvek. Pokud například spustíte následující kód v ladicím programu a vložíte zarážku na řádky auto divisible_by_three = ...
, auto square = ...
uvidíte, že jste narazili divisible_by_three
na zarážku lambda, protože každý prvek je input
testován pro dělitelnost o tři. Zarážka square
lambda bude hita jako prvky, které jsou dělitelné třemi jsou čtvercové.
// requires /std:c++20
#include <ranges>
#include <vector>
#include <iostream>
int main()
{
std::vector<int> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
auto divisible_by_three = [](const int n) {return n % 3 == 0; };
auto square = [](const int n) {return n * n; };
auto x = input | std::views::filter(divisible_by_three)
| std::views::transform(square);
for (int i : x)
{
std::cout << i << '\n';
}
return 0;
}
Další informace o zobrazeních naleznete v tématu <ranges>
třídy zobrazení.
Adaptéry rozsahu
Adaptéry rozsahu mají rozsah a vytvářejí zobrazení. Adaptéry rozsahu vytvářejí lazily vyhodnocená zobrazení. To znamená, že neúčtují náklady na transformaci každého prvku v rozsahu za účelem vytvoření zobrazení. Za zpracování elementu v zobrazení platíte pouze náklady, když k ho přistupujete.
V předchozím příkladu adaptér rozsahu filter
vytvoří zobrazení s názvem input
, které obsahuje prvky, které jsou dělitelné třemi. Adaptér transform
rozsahu má zobrazení prvků dělitelné třemi a vytvoří zobrazení těchto prvků čtverců.
Adaptéry rozsahu mohou být zřetězený (složené), což je srdce síly a flexibility rozsahů. Vytváření adaptérů rozsahu umožňuje překonat problém, že předchozí algoritmy STL nejsou snadno sestavitelné.
Další informace o vytváření zobrazení naleznete v tématu Adaptéry rozsahu.
Algoritmy rozsahu
Některé algoritmy rozsahu berou argument rozsahu. Příklad: std::ranges::sort(myVector);
.
Algoritmy rozsahu jsou téměř stejné jako odpovídající algoritmy páru iterátoru std
v oboru názvů. Rozdíl je v tom, že mají omezení vynucená konceptem a přijímají buď argumenty rozsahu, nebo více dvojic argumentů iterator-sentinel. Můžou pracovat přímo na kontejneru a můžou být snadno zřetězený.
<ranges>
– funkce
Následující funkce slouží k vytváření iterátorů a sentinelů pro rozsahy a k získání velikosti rozsahu.
Function | Popis |
---|---|
begin C++20 |
Získejte iterátor k prvnímu prvku v oblasti. |
cbegin C++20 |
const Získejte iterátor k prvnímu prvku v oblasti. |
cend C++20 |
Získejte sentinel na konci kvalifikovaného rozsahu const . |
cdata C++20 |
const Získejte ukazatel na první prvek v souvislé oblasti. |
crbegin C++20 |
Na začátek rozsahu získáte reverzní const iterátor. |
crend C++20 |
Získejte sentinel na konci toho, co crbegin() se vrátí. |
data C++20 |
Získejte ukazatel na první prvek v souvislé oblasti. |
empty C++20 |
Určete, jestli je oblast prázdná. |
end C++20 |
Získejte sentinel na konci rozsahu. |
rbegin C++20 |
Na začátek rozsahu získáte reverzní iterátor. |
rend C++20 |
Získejte reverzní iterátor do sentinelu na konci rozsahu. |
size C++20 |
Získá velikost rozsahu jako nepodepsanou hodnotu. |
ssize C++20 |
Získá velikost rozsahu jako podepsanou hodnotu. |
Další informace najdete v tématu <ranges>
funkce.
Koncepty rozsahu
Způsob iterace prvků rozsahu závisí na jeho základním typu iterátoru. Rozsahy používají koncepty jazyka C++, které určují, který iterátor podporují.
V jazyce C++20 řekněme, že koncept X upřesňuje koncept Y znamená, že vše, co splňuje koncept Y, také splňuje koncept X. Například: auto, autobus a nákladní vůz zpřesní vozidlo.
Některé koncepty rozsahu odrážejí hierarchii kategorií iterátoru. Následující tabulka uvádí koncepty rozsahu spolu s typy kontejnerů, na které je možné je použít.
Koncept rozsahu | Popis | Podporované kontejnery |
---|---|---|
std::ranges::output_range |
Může iterovat dopředu. | |
std::ranges::input_range |
Může iterovat od začátku do konce alespoň jednou. | std::forward_list std::unordered_map std::unordered_multimap std::unordered_set std::unordered_multiset basic_istream_view |
std::ranges::forward_range |
Může iterovat od začátku do konce více než jednou. | std::forward_list std::unordered_map std::unordered_multimap std::unordered_set std::unordered_multiset |
std::ranges::bidirectional_range |
Může iterovat dopředu a dozadu více než jednou. | std::list std::map std::multimap std::multiset std::set |
std::ranges::random_access_range |
Pomocí operátoru [] lze získat přístup k libovolnému prvku (v konstantním čase). |
std::deque |
std::ranges::contiguous_range |
Prvky jsou uloženy v paměti po sobě jdoucí. | std::array std::string std::vector |
Další <ranges>
informace o těchto konceptech najdete v konceptech.
<ranges>
šablony aliasů
Následující šablony aliasů určují typy iterátorů a sentinelů pro určitou oblast:
Šablona aliasu | Popis |
---|---|
borrowed_iterator_t C++20 |
Zjistěte, jestli iterátor vrácený range pro oblast, jejíž životnost skončila. |
borrowed_subrange_t C++20 |
Určete, jestli iterátor vrácený subrange pro dílčí uspořádání odkazuje na poduspořádku, jehož životnost skončila. |
dangling C++20 |
Označuje, že vrácený iterátor z doby životarange subrange /, na kterou odkazuje.range /subrange |
iterator_t C++20 |
Vrátí typ iterátoru zadaného typu rozsahu. |
range_difference_t C++20 |
Vrátí typ rozdílu zadaného typu iterátoru rozsahu. |
range_reference_t C++20 |
Vrátí odkazový typ zadaného typu iterátoru rozsahu. |
range_rvalue_reference_t C++20 |
Vrátí typ odkazu rvalue pro zadaný typ iterátoru rozsahu. Jinými slovy, typ odkazu rvalue elementů rozsahu. |
range_size_t C++20 |
Vrátí typ použitý k hlášení zadané velikosti rozsahu. |
range_value_t C++20 |
Vrátí typ hodnoty zadaného typu iterátoru rozsahu. Nebo jinými slovy, typ prvků v oblasti. |
sentinel_t C++20 |
Vrátí typ sentinelu zadaného rozsahu. |
Další informace o těchto šablonách aliasů najdete v tématu <ranges>
šablony aliasů.
Viz také
<ranges>
– funkce
<ranges>
koncepty
Adaptéry rozsahu
Referenční informace k souborům hlaviček