Condividi tramite


<ranges> classi di visualizzazione

Una visualizzazione è un intervallo leggero che fa riferimento a elementi che non è proprietario (ad eccezione owning_viewdi ). Una visualizzazione è in genere basata su un altro intervallo e offre un modo diverso di esaminarlo, sia trasformandolo o filtrandolo. Ad esempio, std::views::filter è una visualizzazione che usa i criteri specificati per selezionare gli elementi da un altro intervallo.

Quando si accede agli elementi in una visualizzazione, viene eseguita "pigrizia" in modo che il lavoro venga eseguito solo quando si ottiene un elemento. In questo modo è possibile combinare o comporre visualizzazioni senza una riduzione delle prestazioni.

Ad esempio, è possibile creare una visualizzazione che fornisca solo gli elementi pari da un intervallo e quindi trasformarli con un quadrato. Il lavoro da eseguire per filtrare e trasformare viene eseguito solo per gli elementi a cui si accede e solo quando si accede a tali elementi.

Una visualizzazione può essere copiata, assegnata ed eliminata in tempo costante indipendentemente dal numero di elementi che contiene. Ciò è dovuto al fatto che una visualizzazione non è proprietaria degli elementi a cui fa riferimento, quindi non è necessario creare una copia. Questo è il motivo per cui è possibile comporre visualizzazioni senza una riduzione delle prestazioni.

In genere si crea una visualizzazione usando un adattatore di intervallo. Gli adattatori di intervallo sono il modo previsto per creare una visualizzazione, sono più facili da usare rispetto alla creazione diretta di istanze delle classi di visualizzazione e talvolta sono più efficienti rispetto alla creazione di istanze dirette delle classi di visualizzazione. Le classi di visualizzazione vengono esposte direttamente nel caso in cui sia necessario creare un tipo di visualizzazione personalizzato in base a un tipo di visualizzazione esistente.

Di seguito è riportato un breve esempio di creazione di una visualizzazione dei quadrati degli elementi divisibile per tre in un vettore:

// requires /std:c++20 or later
#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 << ' '; // 0 9 36 81
    }
}
0 9 36 81

L'uso di una visualizzazione dopo l'intervallo su cui è basato può causare un comportamento non definito. Ad esempio, un reverse_view oggetto basato su un vettore non deve essere riutilizzato se si aggiungono o rimuovono elementi dal vettore sottostante. La modifica del vettore sottostante invalida l'iteratore del end contenitore, inclusa la copia dell'iteratore che potrebbe essere stata eseguita dalla vista.

Poiché le visualizzazioni sono economiche da creare, in genere è consigliabile ricreare una visualizzazione se si modifica l'intervallo sottostante. Nell'esempio seguente viene illustrato come archiviare una pipeline di visualizzazione in una variabile in modo da poterla riutilizzare.

// requires /std:c++20, or later
#include <iostream>
#include <ranges>
#include <vector>
#include <list>
#include <string_view>
#include <algorithm>

template<typename rangeType>
void show(std::string_view msg, rangeType r)
{
    std::cout << msg;
    std::ranges::for_each(r,
        [](auto e)
        {
            std::cout << e << ' ';
        });
    std::cout << '\n';
}

int main()
{
    std::vector v{ 1, 2, 3, 4 };
    show("v: ", v);

    // You can save a view pipeline
    auto rev3 = std::views::take(3) | std::views::reverse;

    show("v | rev3: ", v | rev3); // 3 2 1

    v.insert(v.begin(), 0); // v = 0 1 2 3 4
    show("v: ", v);

    // Because modifying the vector invalidates its iterators, rebuild the view.
    // We are reusing the view pipeline we saved earlier
    show("v | rev3(v): ", rev3(v));
}
v: 1 2 3 4
v | rev3: 3 2 1
v: 0 1 2 3 4
v | rev3(v): 2 1 0

Le classi di visualizzazione seguenti sono definite nello spazio dei std::ranges nomi .

Visualizza Descrizione
basic_istream_viewC++20 Visualizzazione degli elementi successivi da un flusso di input. Le specializzazioni includono istream_view e wistream_view.
common_viewC++20 Adatta una visualizzazione con tipi iteratori/sentinel diversi in una visualizzazione con gli stessi tipi iteratore/sentinel.
drop_viewC++20 Creato da un'altra visualizzazione, ignorando i primi count elementi.
drop_while_viewC++20 Creato da un'altra visualizzazione, ignorando gli elementi iniziali purché si blocchi un predicato.
elements_viewC++20 Vista sull'indice selezionato in ogni valore simile a una tupla in una raccolta. Ad esempio, dato un intervallo di std::tuple<string, int> valori, creare una vista costituita da tutti gli string elementi di ogni tupla.
empty_viewC++20 Visualizzazione senza elementi.
filter_viewC++20 Filtra gli elementi di un intervallo che non corrispondono a un predicato.
iota_viewC++20 Vista generata che contiene una sequenza di valori incrementati.
join_viewC++20 Combina tutti gli elementi di più intervalli in una singola visualizzazione.
keys_viewC++20 Vista sul primo indice in ogni valore simile a una tupla in una raccolta. Ad esempio, dato un intervallo di std::tuple<string, int> valori, creare una vista costituita dagli elementi di string ogni tupla.
lazy_split_viewC++20 Suddivide una visualizzazione in intervalli secondari in base a un delimitatore.
owning_viewC++20 Acquisisce la proprietà degli elementi da un altro intervallo.
ref_viewC++20 Visualizzazione che fa riferimento agli elementi appartenenti a un altro intervallo.
reverse_viewC++20 Presenta gli elementi di un intervallo in ordine inverso.
single_viewC++20 Visualizzazione che contiene un solo elemento.
split_viewC++20 Suddivide una visualizzazione in intervalli secondari in base a un delimitatore.
subrangeC++20 Visualizzazione di parte degli elementi di un intervallo, come definito da un iteratore iniziale e da una sentinella.
take_viewC++20 Contiene il numero specificato di elementi ricavati dalla parte anteriore di un intervallo.
take_while_viewC++20 Contiene gli elementi iniziali di un intervallo che corrispondono al predicato specificato.
transform_viewC++20 Visualizzazione di una sequenza sottostante dopo l'applicazione di una funzione di trasformazione a ogni elemento.
values_viewC++20 Una vista sul secondo indice in ogni valore simile a una tupla in una raccolta. Ad esempio, dato un intervallo di std::tuple<string, int> valori, creare una vista costituita dagli elementi di int ogni tupla.

Molte di queste classi hanno adattatori di intervallo corrispondenti nello std:views spazio dei nomi che crea istanze di tali classi. Preferire l'uso di un adattatore per creare una vista anziché creare direttamente classi di visualizzazione. Gli adattatori di intervallo sono il modo previsto per creare visualizzazioni, sono più facili da usare e in alcuni casi sono più efficienti.

Visualizzare le caratteristiche delle classi

Ogni argomento della classe di visualizzazione include una sezione Caratteristiche dopo la sezione della sintassi. La sezione Caratteristiche contiene le voci seguenti:

  • Adattatore di intervallo: collegamento all'adattatore di intervallo che crea la visualizzazione. In genere si usa un adattatore di intervallo per creare una visualizzazione anziché creare direttamente una classe di visualizzazione, quindi viene elencata qui per praticità.

  • Intervallo sottostante: le viste hanno requisiti di iteratore diversi per il tipo di intervallo sottostante che possono usare. Per altre informazioni sui tipi di iteratori, vedere Gerarchia degli iteratori di intervalli.

  • Visualizza categoria iteratore: categoria iteratore della visualizzazione. Quando una vista si adatta a un intervallo, il tipo di iteratore per la visualizzazione è in genere uguale al tipo di iteratore dell'intervallo sottostante. Tuttavia, potrebbe essere diverso per alcune visualizzazioni. Ad esempio, reverse_view ha un bidirectional_iteratoroggetto , anche se l'intervallo sottostante ha un oggetto random_access_iterator.

  • Tipo di elemento: tipo degli elementi restituiti dall'iteratore della visualizzazione.

  • Dimensioni: indica se la visualizzazione può restituire il numero di elementi a cui fa riferimento. Non tutte le visualizzazioni possono.

  • Intervallo comune: specifica se la vista è un common_rangeoggetto , il che significa che gli iteratori di inizio e i tipi sentinel sono gli stessi. Gli intervalli comuni sono utili per il codice pre-intervallo che funziona con le coppie di iteratori. Un esempio è costituito dai costruttori di coppie di iteratori per un contenitore di sequenze, ad esempio vector(ranges::begin(x), ranges::end(x)).

  • Intervallo preso in prestito: specifica se la vista è un intervallo preso in prestito. borrowed_range<T> significa che è possibile usare iteratori per T dopo T che è stato eliminato.

    Nessun contenitore standard è un intervallo preso in prestito, perché l'eliminazione del contenitore libera gli elementi e invalida gli iteratori. In tal caso, diciamo che gli iteratori vengono lasciati "infastidati" dopo la distruzione.

    Ad esempio, std::ranges::find() restituisce in genere un iteratore all'elemento trovato nell'argomento intervallo. Se l'argomento range è un contenitore temporaneo (rvalue), è un errore archiviare l'iteratore restituito e usarlo in un secondo momento perché è "dangling".

    Gli algoritmi di intervallo che restituiscono iteratori (o intervalli secondari) lo fanno solo quando i relativi argomenti sono lvalue (non temporanei) o intervalli presi in prestito. In caso contrario, restituiscono un std::dangling oggetto , che fornisce un suggerimento nei messaggi di errore relativi a ciò che è andato storto se si è tentato di usarlo come un iteratore.

  • Iterabileconst: indica se è possibile scorrere un'istanza const della vista. Non tutte le const visualizzazioni possono essere iterate. Se una vista non const è iterabile, non è possibile eseguire l'iterazione o for (const auto& element : as_const(theView)) passarla a una funzione che accetta un const riferimento alla vista e quindi tenta di eseguire l'iterazione su di essa.

Gerarchia dell'iteratore degli intervalli

Nella sezione Caratteristiche di ogni argomento della classe di visualizzazione, la categoria iteratore in Intervallo sottostante e Categoria iteratore view fa riferimento al tipo di iteratore supportato dall'intervallo/visualizzazione. Esistono sei categorie di iteratori di intervalli, identificati dai concetti di C++20. La gerarchia degli iteratori di intervallo, in ordine crescente di funzionalità, è:

Concetto di iteratore di intervallo Descrizione
output_range Solo scrittura, si sposta solo in avanti; pass singolo.
input_range Sola lettura, si sposta solo in avanti; pass singolo.
forward_range Si sposta solo in avanti; multi-pass.
bidirectional_range Può spostarsi avanti e indietro; multi-pass.
random_access_range Può accedere alla raccolta con un indice; multi-pass.
contiguous_range È possibile accedere alla raccolta con un indice e gli elementi vengono archiviati in modo contiguo in memoria.

In generale, un iteratore ha la funzionalità degli iteratori che lo precedono nella tabella. Ad esempio, bidirectional_range ha le funzionalità di forward_range, ma non viceversa. Ad eccezione input_rangedi , che non ha la funzionalità di output_range perché non è possibile scrivere in un oggetto input_range.

L'istruzione "richiede input_range o versione successiva" indica che la vista può essere usata con un input_rangeiteratore , forward_rangerandom_access_rangebidirectional_range, , o contiguous_range , perché sono tutti in grado di usare .input_range

La gerarchia degli intervalli iteratore è direttamente correlata alla gerarchia dell'iteratore. Per altre informazioni, vedere Concetti relativi all'iteratore.

Vedi anche

<ranges>
Adattatori di intervallo