Concetti relativi all'iteratore
I concetti sono una funzionalità del linguaggio C++20 che vincola i parametri del modello in fase di compilazione. Consentono di evitare la creazione di istanze di modelli non corretti, specificare i requisiti degli argomenti del modello in un modulo leggibile e fornire altri errori del compilatore correlati al modello.
Si consideri l'esempio seguente, che definisce un concetto per impedire la creazione di istanze del modello con un tipo che non supporta la divisione:
// requires /std:c++20 or later
#include <iostream>
// Definition of dividable concept which requires
// that arguments a & b of type T support division
template <typename T>
concept dividable = requires (T a, T b)
{
a / b;
};
// Apply the concept to a template.
// The template will only be instantiated if argument T supports division.
// This prevents the template from being instantiated with types that don't support division.
// This could have been applied to the parameter of a template function, but because
// most of the concepts in the <ranges> library are applied to classes, this form is demonstrated.
template <class T> requires dividable<T>
class DivideEmUp
{
public:
T Divide(T x, T y)
{
return x / y;
}
};
int main()
{
DivideEmUp<int> dividerOfInts;
std::cout << dividerOfInts.Divide(6, 3); // outputs 2
// The following line will not compile because the template can't be instantiated
// with char* because char* can be divided
DivideEmUp<char*> dividerOfCharPtrs; // compiler error: cannot deduce template arguments
}
Quando si passa l'opzione /diagnostics:caret
del compilatore a Visual Studio 2022 versione 17.4 preview 4 o successiva, l'errore valutato dividable<char*>
su false punterà direttamente al requisito (a / b)
dell'espressione che non è riuscito.
I concetti dell'iteratore vengono definiti nello spazio dei std
nomi e vengono dichiarati nel <iterator>
file di intestazione. Vengono usate nelle dichiarazioni di adattatori di intervallo, visualizzazioni e così via.
Esistono sei categorie di iteratori. Sono direttamente correlati alle categorie di intervalli elencati in Concetti relativi all'intervallo.
I concetti dell'iteratore seguenti sono elencati in ordine di aumento delle funzionalità. input_or_output_iterator
è alla fine bassa della gerarchia delle funzionalità ed contiguous_iterator
è all'estremità alta. Gli iteratori più alti nella gerarchia possono essere in genere usati al posto di quelli inferiori, ma non viceversa. Ad esempio, un random_access_iterator
iteratore può essere usato al posto di un forward_iterator
oggetto , ma non in altro modo. Un'eccezione è input_iterator
, che non può essere usata al posto di output_iterator
perché non può scrivere.
Nella tabella seguente , "Multi-pass" indica se l'iteratore può rivedere lo stesso elemento più di una volta. Ad esempio, vector::iterator
è un iteratore a più passaggi perché è possibile creare una copia dell'iteratore, leggere gli elementi nella raccolta e quindi ripristinare l'iteratore sul valore nella copia e rivedere nuovamente gli stessi elementi. Se un iteratore è a passaggio singolo, è possibile visitare solo gli elementi nella raccolta una sola volta.
Nella tabella seguente "Tipi di esempio" si riferisce a raccolte/iteratori che soddisfano il concetto.
Concetto di iteratore | Descrizione | Direzione | Lettura/scrittura | Multi-pass | Tipi di esempio |
---|---|---|---|---|---|
input_or_output_iterator C++20 |
Base della tassonomia del concetto di iteratore. | Inoltra | Lettura/scrittura | no | istream_iterator , ostream_iterator |
output_iterator C++20 |
Specifica un iteratore in cui è possibile scrivere. | Inoltra | Scrittura | no | ostream , inserter |
input_iterator C++20 |
Specifica un iteratore da cui è possibile leggere una sola volta. | Inoltra | Lettura | no | istream , istreambuf_iterator |
forward_iterator C++20 |
Specifica un iteratore in grado di leggere (ed eventualmente scrivere) più volte. | Inoltra | Lettura/scrittura | yes | vector , list |
bidirectional_iterator C++20 |
Specifica un iteratore che è possibile leggere e scrivere sia avanti che indietro. | Avanti o indietro | Lettura/scrittura | yes | list , set , multiset , map e multimap . |
random_access_iterator C++20 |
Specifica un iteratore che è possibile leggere e scrivere in base all'indice. | Avanti o indietro | Lettura/scrittura | yes | vector , array , deque |
contiguous_iterator C++20 |
Specifica un iteratore i cui elementi sono sequenziali in memoria, sono le stesse dimensioni e possono essere accessibili usando l'aritmetica del puntatore. | Avanti o indietro | Lettura/scrittura | yes | array , vector string . |
Altri concetti dell'iteratore includono:
Concetto di iteratore | Descrizione |
---|---|
sentinel_for C++20 |
Specifica che un tipo è un sentinel per un tipo di iteratore. |
sized_sentinel_for C++20 |
Specifica che un iteratore e il relativo sentinella possono essere sottratti (usando - ) per trovare la differenza nel tempo costante. |
bidirectional_iterator
Un bidirectional_iterator
oggetto supporta la lettura e la scrittura avanti e indietro.
template<class I>
concept bidirectional_iterator =
forward_iterator<I> &&
derived_from<ITER_CONCEPT(I), bidirectional_iterator_tag> &&
requires(I i) {
{--i} -> same_as<I&>;
{i--} -> same_as<I>;
};
Parametri
I
Iteratore da testare per verificare se si tratta di un oggetto bidirectional_iterator
.
Osservazioni:
Un bidirectional_iterator
oggetto ha le funzionalità di un forward_iterator
oggetto , ma può anche eseguire un'iterazione all'indietro.
Alcuni esempi di contenitori che possono essere usati con un bidirectional_iterator
sono set
, , multimap
vector
multiset
map
e .list
Esempio: bidirectional_iterator
Nell'esempio seguente viene usato il bidirectional_iterator
concetto per indicare che vector<int>
ha un oggetto bidirectional_iterator
:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
std::cout << std::boolalpha << std::bidirectional_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::contiguous_iterator<decltype(v)::iterator>; // outputs true
}
contiguous_iterator
Specifica un iteratore i cui elementi sono sequenziali in memoria, sono le stesse dimensioni e possono essere accessibili usando l'aritmetica del puntatore.
template<class I>
concept contiguous_iterator =
random_access_iterator<I> &&
derived_from<ITER_CONCEPT(I), contiguous_iterator_tag> &&
is_lvalue_reference_v<iter_reference_t<I>> &&
same_as<iter_value_t<I>, remove_cvref_t<iter_reference_t<I>>> &&
requires(const I& i) {
{ to_address(i) } -> same_as<add_pointer_t<iter_reference_t<I>>>;
};
Parametri
I
Tipo da testare per verificare se si tratta di un oggetto contiguous_iterator
.
Osservazioni:
È possibile accedere a un contiguous_iterator
oggetto tramite aritmetica del puntatore perché gli elementi sono disposti in modo sequenziale in memoria e hanno le stesse dimensioni. Alcuni esempi di sono contiguous_iterator
array
, vector
e string
.
Esempio: contiguous_iterator
Nell'esempio seguente viene usato il contiguous_iterator
concetto per indicare che un vector<int>
oggetto ha un oggetto contiguous_iterator
:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector<int> has a contiguous_iterator
std::cout << std::boolalpha << std::contiguous_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::contiguous_iterator<decltype(v)::iterator>; // outputs true
}
forward_iterator
Dispone delle funzionalità di un oggetto input_iterator
e di un oggetto output_iterator
. Supporta l'iterazione su una raccolta più volte.
template<class I>
concept forward_iterator =
input_iterator<I> &&
derived_from<ITER_CONCEPT(I), forward_iterator_tag> &&
incrementable<I> &&
sentinel_for<I, I>;
Parametri
I
Iteratore da testare per verificare se si tratta di un oggetto forward_iterator
.
Osservazioni:
Un forward_iterator
oggetto può spostarsi solo in avanti.
Alcuni esempi di contenitori che possono essere usati con un forward_iterator
sono vector
, , unordered_multiset
unordered_map
list
unordered_set
e .unordered_multimap
Esempio: forward_iterator
Nell'esempio seguente viene usato il forward_iterator
concetto per indicare che un vector<int>
oggetto ha un oggetto forward_iterator
:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector has a forward_iterator
std::cout << std::boolalpha << std::forward_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::forward_iterator<decltype(v)::iterator>; // outputs true
}
input_iterator
È input_iterator
un iteratore che è possibile leggere almeno una volta.
template<class I>
concept input_iterator =
input_or_output_iterator<I> &&
indirectly_readable<I> &&
requires { typename ITER_CONCEPT(I); } &&
derived_from<ITER_CONCEPT(I), input_iterator_tag>;
Parametri
I
Tipo da testare per verificare se si tratta di un oggetto input_iterator
.
Osservazioni:
La chiamata begin()
a più input_iterator
volte comporta un comportamento non definito. Un tipo che solo i modelli input_iterator
non sono multi-pass. Si consideri ad esempio la lettura dall'input standard (cin
). In questo caso, è possibile leggere l'elemento corrente una sola volta e non è possibile leggere nuovamente i caratteri già letti. Un input_iterator
solo legge in avanti, non all'indietro.
Esempio: input_iterator
Nell'esempio seguente viene usato il input_iterator
concetto per indicare che un oggetto istream_iterator
ha un oggetto input_iterator
:
// requires /std:c++20 or later
#include <iostream>
int main()
{
// Show that a istream_iterator has an input_iterator
std::cout << std::boolalpha << std::input_iterator<std::istream_iterator<int>>; // outputs true
}
input_or_output_iterator
Un input_or_output_iterator
è la base della tassonomia del concetto di iteratore. Supporta la dereferenziazione e l'incremento di un iteratore. Ogni iteratore modella input_or_output_iterator
.
template<class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> can-reference;
} &&
weakly_incrementable<I>;
Parametri
I
Tipo da testare per verificare se si tratta di un oggetto input_or_output_iterator
.
Osservazioni:
Il concetto can-reference
significa che il tipo I
è un riferimento, un puntatore o un tipo che può essere convertito in modo implicito in un riferimento.
Esempio: input_or_output_iterator
Nell'esempio seguente viene usato il input_or_output_iterator
concetto per indicare che vector<int>
ha un oggetto input_or_output_iterator
:
// requires /std:c++20 or later
#include <iostream>
int main()
{
// Show that a vector has an input_or_output_iterator
std::cout << std::boolalpha << std::input_or_output_iterator<std::vector<int>::iterator> << '\n'; // outputs true
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::input_or_output_iterator<decltype(v)::iterator>; // outputs true
}
output_iterator
È output_iterator
un iteratore in cui è possibile scrivere.
template<class I, class T>
concept output_iterator =
input_or_output_iterator<I> &&
indirectly_writable<I, T> &&
requires(I i, T&& t) {
*i++ = std::forward<T>(t);
};
Parametri
I
Tipo da testare per verificare se si tratta di un oggetto output_iterator
.
T
Tipo dei valori da scrivere.
Osservazioni:
È output_iterator
un singolo passaggio. Ovvero, può scrivere nello stesso elemento una sola volta.
Esempio: output_iterator
Nell'esempio seguente viene usato il output_iterator
concetto per indicare che vector<int>
ha un oggetto output_iterator
:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector<int> has an output_iterator
std::cout << std::boolalpha << std::output_iterator<std::vector<int>::iterator, int> << "\n"; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2,3,4,5};
std::cout << std::boolalpha << std::output_iterator<decltype(v)::iterator, int>; // outputs true
}
random_access_iterator
Un random_access_iterator
oggetto può leggere o scrivere in base all'indice.
template<class I>
concept random_access_iterator =
bidirectional_iterator<I> &&
derived_from<ITER_CONCEPT(I), random_access_iterator_tag> &&
totally_ordered<I> &&
sized_sentinel_for<I, I> &&
requires(I i, const I j, const iter_difference_t<I> n) {
{ i += n } -> same_as<I&>;
{ j + n } -> same_as<I>;
{ n + j } -> same_as<I>;
{ i -= n } -> same_as<I&>;
{ j - n } -> same_as<I>;
{ j[n] } -> same_as<iter_reference_t<I>>;
};
Parametri
I
Tipo da testare per verificare se si tratta di un oggetto random_access_iterator
.
Osservazioni:
Un random_access_iterator
oggetto ha le funzionalità di un input_iterator
oggetto , output_iterator
, forward_iterator
e bidirectional_iterator
.
Alcuni esempi di sono random_access_iterator
vector
, array
e deque
.
Esempio: random_access_iterator
L'esempio seguente mostra che un oggetto vector<int>
ha un oggetto random_access_iterator
:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
// Show that vector<int> has a random_access_iterator
std::cout << std::boolalpha << std::random_access_iterator<std::vector<int>::iterator> << '\n'; // outputs "true"
// another way to test
std::vector<int> v = {0,1,2};
std::cout << std::boolalpha << std::random_access_iterator<decltype(v)::iterator>; // outputs true
}
sentinel_for
Specifica che un tipo è un sentinel per un iteratore.
template<class S, class I>
concept sentinel_for =
semiregular<S> &&
input_or_output_iterator<I> &&
weakly-equality-comparable-with <S, I>;
Parametri
I
Tipo di iteratore.
S
Tipo da testare per verificare se si tratta di un sentinel per I
.
Osservazioni:
Un sentinel è un tipo che può essere confrontato con un iteratore per determinare se l'iteratore ha raggiunto la fine. Questo concetto determina se un tipo è un sentinel per uno dei input_or_output_iterator
tipi, che include input_iterator
, bidirectional_iterator
random_access_iterator
output_iterator
forward_iterator
e .contiguous_iterator
Esempio: sentinel_for
Nell'esempio seguente viene usato il sentinel_for
concetto per indicare che vector<int>::iterator
è un sentinel per vector<int>
:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = {0, 1, 2};
std::vector<int>::iterator i = v.begin();
// show that vector<int>::iterator is a sentinel for vector<int>
std::cout << std::boolalpha << std::sentinel_for<std::vector<int>::iterator, decltype(i)>; // outputs true
}
sized_sentinel_for
Verificare che un iteratore e il relativo sentinella possano essere sottratti usando -
per trovare la differenza, in tempo costante.
template<class S, class I>
concept sized_sentinel_for =
sentinel_for<S, I> &&
!disable_sized_sentinel_for<remove_cv_t<S>, remove_cv_t<I>> &&
requires(const I& i, const S& s) {
{s - i} -> same_as<iter_difference_t<I>>;
{i - s} -> same_as<iter_difference_t<I>>;
};
Parametri
I
Tipo di iteratore.
S
Tipo di sentinel da testare.
Osservazioni:
Esempio: sized_sentinel_for
Nell'esempio seguente viene usato il sized_sentinel_for
concetto per verificare che l'iteratore sentinel per un vector<int>
oggetto possa essere sottratto dagli iteratori vettoriali in tempo costante:
// requires /std:c++20 or later
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3 };
std::vector<int>::iterator i = v.begin();
std::vector<int>::iterator end = v.end();
// use the sized_sentinel_for concept to verify that i can be subtracted from end in constant time
std::cout << std::boolalpha << std::sized_sentinel_for<decltype(end), decltype(i)> << "\n"; // outputs true
std::cout << end - i; // outputs 3
}
Vedi anche
Concetti relativi all'intervallo
Adattatori di intervallo
Visualizzare le classi