<ranges>
A livello generale, un intervallo è qualcosa che è possibile scorrere. Un intervallo è rappresentato da un iteratore che contrassegna l'inizio dell'intervallo e un sentinella che contrassegna la fine dell'intervallo. Sentinel può essere lo stesso tipo dell'iteratore di inizio oppure può essere diverso. I contenitori, ad esempio vector
e list
, nella libreria standard C++ sono intervalli. Un intervallo astrae gli iteratori in modo da semplificare e amplificare la possibilità di usare la libreria di modelli standard (STL).
Gli algoritmi STL in genere accettano iteratori che puntano alla parte della raccolta su cui devono operare. Si consideri, ad esempio, come ordinare un vector
oggetto usando std::sort()
. Si passano due iteratori che contrassegnano l'inizio e la fine dell'oggetto vector
. Ciò offre flessibilità, ma il passaggio degli iteratori all'algoritmo è un lavoro aggiuntivo perché probabilmente si vuole solo ordinare l'intera cosa.
Con gli intervalli, è possibile chiamare std::ranges::sort(myVector);
, che viene considerato come se si chiamasse std::sort(myVector.begin(), myVector.end());
. Nelle librerie di intervalli, gli algoritmi accettano intervalli come parametri (anche se possono accettare iteratori, se lo si desidera). Possono operare direttamente sulle raccolte. Esempi di algoritmi di intervallo disponibili in <algorithm>
includono copy
, copy_n
copy_if
, all_of
, , any_of
none_of
, find
, count
find_if
find_if_not
for_each
for_each_n
count_if
, equal
e .mismatch
Ma forse il vantaggio più importante degli intervalli è che è possibile comporre algoritmi STL che operano su intervalli in uno stile che ricorda la programmazione funzionale.
Esempio di intervalli
Prima degli intervalli, se si desidera trasformare gli elementi di una raccolta che soddisfa un determinato criterio, è necessario introdurre un passaggio intermedio per contenere i risultati tra le operazioni. Ad esempio, se si vuole creare un vettore di quadrati dagli elementi in un altro vettore divisibile per tre, è possibile scrivere qualcosa di simile al seguente:
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; });
Con gli intervalli, è possibile eseguire la stessa operazione senza bisogno del intermediate
vettore:
// 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; });
Oltre a essere più facile da leggere, questo codice evita l'allocazione di memoria necessaria per il vettore e il intermediate
relativo contenuto. Consente inoltre di comporre due operazioni.
Nel codice precedente, ogni elemento divisibile per tre viene combinato con un'operazione al quadrato di tale elemento. Il simbolo pipe (|
) concatena le operazioni e viene letto da sinistra a destra.
Il risultato, output
, è stesso un tipo di intervallo denominato visualizzazione.
Visualizzazioni
Una visualizzazione è un intervallo leggero. Visualizzare operazioni, ad esempio costruzione predefinita, costruzione/assegnazione di spostamento, costruzione/assegnazione di copia (se presente), distruzione, inizio e fine-tutto avviene in tempo costante indipendentemente dal numero di elementi nella visualizzazione.
Le visualizzazioni vengono create da adattatori di intervallo, descritti nella sezione seguente. Per altre informazioni sulle classi che implementano varie visualizzazioni, vedere Visualizzare le classi.
La modalità di visualizzazione degli elementi nella visualizzazione dipende dall'adattatore di intervallo usato per creare la visualizzazione. Nell'esempio precedente, un adattatore di intervallo accetta un intervallo e restituisce una visualizzazione degli elementi divisibile per tre. L'intervallo sottostante rimane invariato.
Le visualizzazioni sono componibili, che è potente. Nell'esempio precedente, la visualizzazione degli elementi vettoriali divisibile per tre viene combinata con la visualizzazione che quadratia tali elementi.
Gli elementi di una vista vengono valutati in modo differito. Ovvero, le trasformazioni applicate a ogni elemento in una visualizzazione non vengono valutate fino a quando non si richiede l'elemento. Ad esempio, se si esegue il codice seguente in un debugger e si inserisce un punto di interruzione nelle righe auto divisible_by_three = ...
e auto square = ...
, si noterà che si raggiunge il divisible_by_three
punto di interruzione lambda perché ogni elemento in input
viene testato per la divisibilebilità per tre. Il square
punto di interruzione lambda verrà raggiunto come gli elementi divisibile per tre sono quadrati.
// 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;
}
Per altre informazioni sulle visualizzazioni, vedere <ranges>
Visualizzare le classi.
Adattatori di intervallo
Gli adattatori di intervallo accettano un intervallo e producono una visualizzazione. Gli adattatori di intervallo producono visualizzazioni valutate in modo differito. Ciò significa che non si comporta il costo della trasformazione di ogni elemento nell'intervallo per produrre la visualizzazione. Si paga solo il costo per elaborare un elemento nella visualizzazione quando si accede a tale elemento.
Nell'esempio precedente, l'adattatore dell'intervallo filter
crea una vista denominata input
che contiene gli elementi divisibile per tre. L'adattatore transform
di intervallo visualizza gli elementi divisibile per tre e crea una visualizzazione di tali elementi quadrati.
Gli adattatori di intervallo possono essere concatenati (composti), che è il cuore della potenza e della flessibilità degli intervalli. La composizione degli adattatori di intervallo consente di risolvere il problema che gli algoritmi STL precedenti non sono facilmente componibili.
Per altre informazioni sulla creazione di visualizzazioni, vedere Adattatori di intervallo.
Algoritmi di intervallo
Alcuni algoritmi di intervallo accettano un argomento di intervallo. Un esempio è std::ranges::sort(myVector);
.
Gli algoritmi di intervallo sono quasi identici agli algoritmi di coppia di iteratori corrispondenti nello spazio dei std
nomi . La differenza è che hanno vincoli applicati dal concetto e accettano argomenti di intervallo o più coppie di argomenti iteratore-sentinel. Possono lavorare direttamente in un contenitore e possono essere facilmente concatenati.
<ranges>
funzioni
Le funzioni seguenti vengono usate per creare iteratori e sentinelle per intervalli e per ottenere le dimensioni di un intervallo.
Funzione | Descrizione |
---|---|
begin C++20 |
Ottenere un iteratore al primo elemento dell'intervallo. |
cbegin C++20 |
Ottenere un const iteratore al primo elemento dell'intervallo. |
cend C++20 |
Ottenere la sentinella alla fine dell'intervallo const qualificato. |
cdata C++20 |
Ottenere un const puntatore al primo elemento nell'intervallo contiguo. |
crbegin C++20 |
Ottiene un iteratore inverso const all'inizio dell'intervallo. |
crend C++20 |
Ottenere l'sentinel alla fine di ciò che crbegin() restituisce. |
data C++20 |
Ottenere un puntatore al primo elemento nell'intervallo contiguo. |
empty C++20 |
Determinare se l'intervallo è vuoto. |
end C++20 |
Ottenere la sentinella alla fine dell'intervallo. |
rbegin C++20 |
Ottiene un iteratore inverso all'inizio dell'intervallo. |
rend C++20 |
Ottenere un iteratore inverso alla sentinella alla fine dell'intervallo. |
size C++20 |
Ottenere le dimensioni dell'intervallo come valore senza segno. |
ssize C++20 |
Ottenere le dimensioni dell'intervallo come valore con segno. |
Per altre informazioni, vedere <ranges>
Funzioni.
Concetti relativi all'intervallo
La modalità di iterazione degli elementi di un intervallo dipende dal tipo di iteratore sottostante. Gli intervalli usano concetti C++ che specificano l'iteratore supportato.
In C++20, per dire che il concetto X affina il concetto Y significa che tutto ciò che soddisfa il concetto Y soddisfa anche il concetto X. Ad esempio: auto, autobus e camion tutti affinano il veicolo.
Alcuni concetti di intervallo rispecchiano la gerarchia delle categorie di iteratori. Nella tabella seguente sono elencati i concetti relativi all'intervallo, insieme ai tipi di contenitori a cui possono essere applicati.
Concetto di intervallo | Descrizione | Contenitori supportati |
---|---|---|
std::ranges::output_range |
Può scorrere in avanti. | |
std::ranges::input_range |
Può eseguire l'iterazione dall'inizio alla fine almeno una volta. | std::forward_list std::unordered_map std::unordered_multimap std::unordered_set std::unordered_multiset basic_istream_view |
std::ranges::forward_range |
Può eseguire l'iterazione dall'inizio alla fine più di una volta. | std::forward_list std::unordered_map std::unordered_multimap std::unordered_set std::unordered_multiset |
std::ranges::bidirectional_range |
Può eseguire l'iterazione in avanti e indietro più volte. | std::list std::map std::multimap std::multiset std::set |
std::ranges::random_access_range |
È possibile accedere a un elemento arbitrario (in tempo costante) usando l'operatore [] . |
std::deque |
std::ranges::contiguous_range |
Gli elementi vengono archiviati in memoria consecutivamente. | std::array std::string std::vector |
Per altre informazioni su questi concetti, vedere <ranges>
concetti .
<ranges>
modelli alias
I modelli di alias seguenti determinano i tipi di iteratori e sentinelle per un intervallo:
Modello di alias | Descrizione |
---|---|
borrowed_iterator_t C++20 |
Determinare se un iteratore restituito per un range fa riferimento a un intervallo la cui durata è terminata. |
borrowed_subrange_t C++20 |
Determinare se un iteratore restituito per un subrange fa riferimento a un intervallo secondario la cui durata è terminata. |
dangling C++20 |
Indica che l'iteratore restituito di un range /subrange oggetto dura la durata dell'oggetto range /subrange a cui fa riferimento. |
iterator_t C++20 |
Restituisce il tipo di iteratore del tipo di intervallo specificato. |
range_difference_t C++20 |
Restituisce il tipo di differenza del tipo iteratore dell'intervallo specificato. |
range_reference_t C++20 |
Restituisce il tipo riferimento del tipo iteratore dell'intervallo specificato. |
range_rvalue_reference_t C++20 |
Restituisce il tipo di riferimento rvalue per il tipo di iteratore dell'intervallo specificato. In altre parole, il tipo di riferimento rvalue degli elementi dell'intervallo. |
range_size_t C++20 |
Restituisce il tipo utilizzato per segnalare le dimensioni dell'intervallo specificato. |
range_value_t C++20 |
Restituisce il tipo di valore del tipo iteratore dell'intervallo specificato. In altre parole, il tipo degli elementi nell'intervallo. |
sentinel_t C++20 |
Restituisce il tipo sentinel dell'intervallo specificato. |
Per altre informazioni su questi modelli di alias, vedere <ranges>
Modelli di alias.
Vedi anche
Funzioni <ranges>
<ranges>
Concetti
Adattatori di intervallo
Informazioni di riferimento per i file di intestazione