Pojęcia iteracyjne
Pojęcia to funkcja języka C++20, która ogranicza parametry szablonu w czasie kompilacji. Pomagają one zapobiegać wystąpieniu nieprawidłowego szablonu, określać wymagania argumentu szablonu w czytelnym formularzu i zapewniać bardziej zwięzłe błędy kompilatora powiązane z szablonem.
Rozważmy poniższy przykład, który definiuje koncepcję, aby zapobiec utworzeniu wystąpienia szablonu z typem, który nie obsługuje dzielenia:
// 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
}
Po przekazaniu przełącznika /diagnostics:caret
kompilatora do programu Visual Studio 2022 w wersji 17.4 (wersja zapoznawcza 4 lub nowsza) błąd dividable<char*>
oceniany na wartość false będzie wskazywać bezpośrednio na wymaganie (a / b)
wyrażenia, które nie powiodło się.
Pojęcia iteracyjne są definiowane w std
przestrzeni nazw i są deklarowane w pliku nagłówkowym <iterator>
. Są one używane w deklaracjach adapterów zakresów, widoków i tak dalej.
Istnieją sześć kategorii iteratorów. Są one bezpośrednio powiązane z kategoriami zakresów wymienionych w temacie Pojęcia dotyczące zakresu.
Poniższe pojęcia iteracyjne są wymienione w celu zwiększenia możliwości. input_or_output_iterator
znajduje się na niskim końcu hierarchii możliwości i contiguous_iterator
znajduje się na wysokim poziomie. Iteratory wyższe w hierarchii mogą być zwykle używane zamiast tych, które są niższe, ale nie odwrotnie. Na przykład random_access_iterator
iterator może być używany zamiast forward_iterator
elementu , ale nie odwrotnie. Wyjątek to input_iterator
, którego nie można użyć zamiast output_iterator
, ponieważ nie można go zapisać.
W poniższej tabeli wyrażenie "Multi-pass" odnosi się do tego, czy iterator może ponownie wrócić do tego samego elementu więcej niż raz. Na przykład jest iteratorem wieloprzepustowym, vector::iterator
ponieważ można utworzyć kopię iteratora, odczytać elementy w kolekcji, a następnie przywrócić iterator do wartości w kopii i ponownie przejrzeć te same elementy. Jeśli iterator jest jednoprzepustowy, można odwiedzić tylko elementy w kolekcji raz.
W poniższej tabeli "Przykładowe typy" odnosi się do kolekcji/iteratorów, które spełniają koncepcję.
Koncepcja iteratora | opis | Kierunek | Czytaj/zapisz | Multi-pass | Przykładowe typy |
---|---|---|---|---|---|
input_or_output_iterator C++20 |
Podstawa taksonomii koncepcji iteratora. | Do przodu | Czytaj/zapisz | nie | istream_iterator , ostream_iterator |
output_iterator C++20 |
Określa iterator, do którego można napisać. | Do przodu | Write | nie | ostream , inserter |
input_iterator C++20 |
Określa iterator, z którego można odczytać jeden raz. | Do przodu | Przeczytaj | nie | istream , istreambuf_iterator |
forward_iterator C++20 |
Określa iterator, który może odczytywać (i ewentualnie zapisywać) wiele razy. | Do przodu | Czytaj/zapisz | tak | vector , list |
bidirectional_iterator C++20 |
Określa iterator, który można odczytywać i zapisywać zarówno do przodu, jak i do tyłu. | W przód lub w tył | Czytaj/zapisz | tak | list , set , multiset , map i multimap . |
random_access_iterator C++20 |
Określa iterator, który można odczytywać i zapisywać według indeksu. | W przód lub w tył | Czytaj/zapisz | tak | vector , , array deque |
contiguous_iterator C++20 |
Określa iterator, którego elementy są sekwencyjne w pamięci, mają ten sam rozmiar i można uzyskać do niego dostęp przy użyciu arytmetyki wskaźnika. | W przód lub w tył | Czytaj/zapisz | tak | array , vector string . |
Inne pojęcia iteracyjne obejmują:
Koncepcja iteratora | opis |
---|---|
sentinel_for C++20 |
Określa, że typ jest sentynel dla typu iteratora. |
sized_sentinel_for C++20 |
Określa, że iterator i jego sentinel można odjąć (przy użyciu ) - w celu znalezienia różnicy w stałym czasie. |
bidirectional_iterator
Element bidirectional_iterator
obsługuje odczytywanie i zapisywanie do przodu i do tyłu.
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>;
};
Parametry
I
Iterator do testowania, aby sprawdzić, czy jest to element bidirectional_iterator
.
Uwagi
Element ma bidirectional_iterator
możliwości forward_iterator
elementu , ale może również iterować do tyłu.
Niektóre przykłady kontenerów, których można używać z elementami bidirectional_iterator
to set
, , multiset
, map
multimap
, , vector
i list
.
Przykład: bidirectional_iterator
W poniższym przykładzie użyto bidirectional_iterator
koncepcji , aby pokazać, że vector<int>
ma wartość 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
Określa iterator, którego elementy są sekwencyjne w pamięci, mają ten sam rozmiar i można uzyskać do niego dostęp przy użyciu arytmetyki wskaźnika.
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>>>;
};
Parametry
I
Typ do przetestowania, aby sprawdzić, czy jest to contiguous_iterator
.
Uwagi
Dostęp contiguous_iterator
do elementu można uzyskać za pomocą arytmetyki wskaźnika, ponieważ elementy są rozmieszczone sekwencyjnie w pamięci i mają ten sam rozmiar. Niektóre przykłady elementów to contiguous_iterator
array
, vector
i string
.
Przykład: contiguous_iterator
W poniższym przykładzie użyto contiguous_iterator
koncepcji, aby pokazać, że element vector<int>
ma wartość 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
Ma możliwości elementu input_iterator
i .output_iterator
Obsługuje iteracji w kolekcji wiele razy.
template<class I>
concept forward_iterator =
input_iterator<I> &&
derived_from<ITER_CONCEPT(I), forward_iterator_tag> &&
incrementable<I> &&
sentinel_for<I, I>;
Parametry
I
Iterator do testowania, aby sprawdzić, czy jest to element forward_iterator
.
Uwagi
Element forward_iterator
może iść do przodu tylko do przodu.
Niektóre przykłady kontenerów, których można używać z elementami forward_iterator
to vector
, , list
, unordered_set
unordered_multiset
, , unordered_map
i unordered_multimap
.
Przykład: forward_iterator
W poniższym przykładzie użyto forward_iterator
koncepcji, aby pokazać, że element vector<int>
ma wartość 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
Jest input_iterator
to iterator, który można odczytać z co najmniej raz.
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>;
Parametry
I
Typ do przetestowania, aby sprawdzić, czy jest to .input_iterator
Uwagi
Wywołanie begin()
metody na więcej input_iterator
niż raz powoduje niezdefiniowane zachowanie. Typ, który tylko modele input_iterator
nie jest wieloprzepustowy. Rozważ odczytywanie ze standardowych danych wejściowych (cin
) na przykład. W takim przypadku można tylko odczytać bieżący element raz i nie można ponownie odczytać znaków, które zostały już przeczytane. Tylko input_iterator
odczytuje do przodu, a nie do tyłu.
Przykład: input_iterator
W poniższym przykładzie użyto input_iterator
koncepcji, aby pokazać, że element istream_iterator
ma element 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
Jest input_or_output_iterator
podstawą taksonomii koncepcji iteratora. Obsługuje wyłudanie i zwiększanie iteratora. Każdy iterator modeluje input_or_output_iterator
.
template<class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> can-reference;
} &&
weakly_incrementable<I>;
Parametry
I
Typ do przetestowania, aby sprawdzić, czy jest to .input_or_output_iterator
Uwagi
Koncepcja can-reference
oznacza, że typ I
jest odwołaniem, wskaźnikiem lub typem, który można niejawnie przekonwertować na odwołanie.
Przykład: input_or_output_iterator
W poniższym przykładzie użyto input_or_output_iterator
koncepcji , aby pokazać, że vector<int>
ma element 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
An output_iterator
to iterator, do którego można napisać.
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);
};
Parametry
I
Typ do przetestowania, aby sprawdzić, czy jest to .output_iterator
T
Typ wartości do zapisania.
Uwagi
Element output_iterator
jest pojedynczym przekazywaniem. Oznacza to, że może on zapisywać tylko w tym samym elemecie raz.
Przykład: output_iterator
W poniższym przykładzie użyto output_iterator
koncepcji , aby pokazać, że vector<int>
ma element 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
Element random_access_iterator
może odczytywać lub zapisywać według indeksu.
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>>;
};
Parametry
I
Typ do przetestowania, aby sprawdzić, czy jest to random_access_iterator
.
Uwagi
Obiekt random_access_iterator
ma możliwości elementów input_iterator
, , output_iterator
forward_iterator
i bidirectional_iterator
.
Niektóre przykłady elementów to random_access_iterator
vector
, array
i deque
.
Przykład: random_access_iterator
W poniższym przykładzie pokazano, że element vector<int>
ma wartość 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
Określa, że typ jest sentynelem dla iteratora.
template<class S, class I>
concept sentinel_for =
semiregular<S> &&
input_or_output_iterator<I> &&
weakly-equality-comparable-with <S, I>;
Parametry
I
Typ iteratora.
S
Typ do przetestowania, aby sprawdzić, czy jest to sentinel dla elementu I
.
Uwagi
Sentinel to typ, który można porównać z iteratorem w celu określenia, czy iterator osiągnął koniec. Ta koncepcja określa, czy typ jest sentinel dla jednego z input_or_output_iterator
typów, w tym input_iterator
, , output_iterator
, forward_iterator
bidirectional_iterator
, random_access_iterator
, i contiguous_iterator
.
Przykład: sentinel_for
W poniższym przykładzie użyto sentinel_for
koncepcji, aby pokazać, że vector<int>::iterator
jest to sentinel dla vector<int>
elementu :
// 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
Przetestuj, czy iterator i jego sentynel można odjąć za pomocą polecenia -
, aby znaleźć różnicę w stałym czasie.
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>>;
};
Parametry
I
Typ iteratora.
S
Typ sentinel do przetestowania.
Uwagi
Przykład: sized_sentinel_for
W poniższym przykładzie sized_sentinel_for
użyto koncepcji w celu sprawdzenia, czy sentinel dla obiektu vector<int>
można odjąć od iteratora wektorów w stałym czasie:
// 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
}
Zobacz też
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla