Koncepty iterátoru
Koncepty jsou funkce jazyka C++20, která omezuje parametry šablony v době kompilace. Pomáhají zabránit nesprávné instanci šablony, zadat požadavky na argument šablony ve čitelné podobě a poskytnout více stručnější chyby kompilátoru související se šablonou.
Podívejte se na následující příklad, který definuje koncept, který brání vytvoření instance šablony s typem, který nepodporuje dělení:
// 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
}
Když předáte přepínač /diagnostics:caret
kompilátoru do sady Visual Studio 2022 verze 17.4 Preview 4 nebo novější, chyba, která se dividable<char*>
vyhodnotí jako nepravda, bude odkazovat přímo na požadavek (a / b)
výrazu, který selhal.
Koncepty iterátoru std
jsou definovány v oboru názvů a deklarují se v <iterator>
souboru hlaviček. Používají se v deklaraci adaptérů, zobrazení a tak dále.
Existuje šest kategorií iterátorů. Přímo souvisejí s kategoriemi rozsahů uvedených v části Koncepty rozsahů.
Následující koncepty iterátoru jsou uvedeny v pořadí zvýšení schopnosti. input_or_output_iterator
je na nízkém konci hierarchie schopností a contiguous_iterator
je na nejvyšší úrovni. Iterátory vyšší v hierarchii lze obecně použít místo těch, které jsou nižší, ale ne naopak. Iterátor lze například random_access_iterator
použít místo forward_iterator
, ale ne naopak. Výjimkou je input_iterator
to, že místo toho nejde použít output_iterator
, protože nejde napsat.
V následující tabulce "Multi-pass" odkazuje na to, zda iterátor může znovu navštívit stejný prvek více než jednou. Jedná se například o iterátor s více průchody, vector::iterator
protože můžete vytvořit kopii iterátoru, přečíst prvky v kolekci a potom obnovit iterátor na hodnotu v kopii a znovu se vrátit ke stejným prvkům. Pokud je iterátor jedním průchodem, můžete prvky v kolekci navštívit jenom jednou.
V následující tabulce "Příklady typů" odkazuje na kolekce/iterátory, které vyhovují konceptu.
Koncept iterátoru | Description | Směr | Čtení/zápis | Vícenásobný průchod | Příklady typů |
---|---|---|---|---|---|
input_or_output_iterator C++20 |
Základem taxonomie konceptu iterátoru. | Dopředu | Čtení/zápis | ne | istream_iterator , ostream_iterator |
output_iterator C++20 |
Určuje iterátor, do kterého můžete zapisovat. | Dopředu | Write | ne | ostream , inserter |
input_iterator C++20 |
Určuje iterátor, ze kterého můžete číst jednou. | Dopředu | Čteno | ne | istream , istreambuf_iterator |
forward_iterator C++20 |
Určuje iterátor, který může číst (a případně zapisovat) vícekrát. | Dopředu | Čtení/zápis | ano | vector , list |
bidirectional_iterator C++20 |
Určuje iterátor, který můžete číst a zapisovat dopředu i dozadu. | Dopředu nebo dozadu | Čtení/zápis | ano | list , set , multiset , map a multimap . |
random_access_iterator C++20 |
Určuje iterátor, který můžete číst a zapisovat podle indexu. | Dopředu nebo dozadu | Čtení/zápis | ano | vector , , array deque |
contiguous_iterator C++20 |
Určuje iterátor, jehož prvky jsou sekvenční v paměti, mají stejnou velikost a lze k němu přistupovat pomocí aritmetické metody ukazatele. | Dopředu nebo dozadu | Čtení/zápis | ano | array , . vector string |
Mezi další koncepty iterátoru patří:
Koncept iterátoru | Popis |
---|---|
sentinel_for C++20 |
Určuje, že typ je sentinel pro typ iterátoru. |
sized_sentinel_for C++20 |
Určuje, že iterátor a jeho sentinel je možné odečíst (pomocí - ) zjistit jejich rozdíl v konstantním čase. |
bidirectional_iterator
A bidirectional_iterator
podporuje čtení a zápis dopředu a dozadu.
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
Iterátor, který testuje, jestli je to .bidirectional_iterator
Poznámky
A bidirectional_iterator
má schopnosti forward_iterator
, ale může také iterovat zpět.
Některé příklady kontejnerů, které lze použít s a bidirectional_iterator
jsou set
, multiset
, map
, multimap
, vector
, a list
.
Příklad: bidirectional_iterator
Následující příklad používá bidirectional_iterator
koncept, který ukazuje, že vector<int>
má 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
Určuje iterátor, jehož prvky jsou sekvenční v paměti, mají stejnou velikost a lze k němu přistupovat pomocí aritmetické metody ukazatele.
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, který chcete otestovat, abyste zjistili, jestli se jedná o contiguous_iterator
.
Poznámky
K Aritmetice contiguous_iterator
ukazatele lze přistupovat, protože prvky jsou rozloženy postupně v paměti a mají stejnou velikost. Některé příklady contiguous_iterator
jsou array
, vector
a string
.
Příklad: contiguous_iterator
Následující příklad používá contiguous_iterator
koncept, který ukazuje, že vector<int>
má 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
Má schopnosti a input_iterator
output_iterator
. Podporuje iteraci v kolekci několikrát.
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
Iterátor, který testuje, jestli je to .forward_iterator
Poznámky
A forward_iterator
může se pohybovat pouze vpřed.
Některé příklady kontejnerů, které lze použít s a forward_iterator
jsou vector
, list
, unordered_set
, unordered_multiset
, unordered_map
, a unordered_multimap
.
Příklad: forward_iterator
Následující příklad používá forward_iterator
koncept, který ukazuje, že vector<int>
má 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
Je input_iterator
iterátor, ze kterého můžete alespoň jednou číst.
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, který chcete otestovat, abyste zjistili, jestli se jedná o .input_iterator
Poznámky
Volání begin()
na input_iterator
více než jednou způsobí nedefinované chování. Typ, který není vícenásobný input_iterator
. Zvažte například čtení ze standardního vstupu (cin
). V tomto případě můžete jen jednou přečíst aktuální prvek a nemůžete číst znaky, které jste už přečetli. Jen input_iterator
čtení vpřed, ne vzad.
Příklad: input_iterator
Následující příklad používá input_iterator
koncept, který ukazuje, že istream_iterator
má 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
Základem input_or_output_iterator
taxonomie konceptu iterátoru je. Podporuje odvozování a zvýšení iterátoru. Každý iterátor modely input_or_output_iterator
.
template<class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> can-reference;
} &&
weakly_incrementable<I>;
Parametry
I
Typ, který chcete otestovat, abyste zjistili, jestli se jedná o .input_or_output_iterator
Poznámky
Koncept can-reference
znamená, že typ I
je odkaz, ukazatel nebo typ, který lze implicitně převést na odkaz.
Příklad: input_or_output_iterator
Následující příklad používá input_or_output_iterator
koncept, který ukazuje, že vector<int>
má 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
Je output_iterator
iterátor, na který můžete psát.
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, který chcete otestovat, abyste zjistili, jestli se jedná o .output_iterator
T
Typ hodnot, které se mají zapisovat.
Poznámky
Je output_iterator
to jeden průchod. To znamená, že může zapisovat pouze do stejného prvku jednou.
Příklad: output_iterator
Následující příklad používá output_iterator
koncept, který ukazuje, že vector<int>
má 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
Index random_access_iterator
může číst nebo zapisovat.
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, který chcete otestovat, abyste zjistili, jestli se jedná o random_access_iterator
.
Poznámky
A random_access_iterator
má schopnosti input_iterator
, output_iterator
, forward_iterator
a bidirectional_iterator
.
Některé příklady random_access_iterator
jsou vector
, array
a deque
.
Příklad: random_access_iterator
Následující příklad ukazuje, že vector<int>
má 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
Určuje, že typ je sentinel pro iterátor.
template<class S, class I>
concept sentinel_for =
semiregular<S> &&
input_or_output_iterator<I> &&
weakly-equality-comparable-with <S, I>;
Parametry
I
Typ iterátoru.
S
Typ, který chcete otestovat, abyste zjistili, jestli se jedná o sentinel pro I
.
Poznámky
Sentinel je typ, který se dá porovnat s iterátorem a určit, jestli iterátor dosáhl konce. Tento koncept určuje, zda je typ sentinelem pro jeden z input_or_output_iterator
typů, který zahrnuje input_iterator
, output_iterator
, forward_iterator
, bidirectional_iterator
, random_access_iterator
a contiguous_iterator
.
Příklad: sentinel_for
Následující příklad používá sentinel_for
koncept, který ukazuje, že vector<int>::iterator
je sentinel pro 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
Otestujte, že iterátor a jeho sentinel je možné odečíst pomocí k -
nalezení rozdílu v konstantním čase.
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 iterátoru.
S
Typ sentinelu, který se má testovat.
Poznámky
Příklad: sized_sentinel_for
Následující příklad používá sized_sentinel_for
koncept k ověření, že sentinel pro a vector<int>
může být odečten od iterátoru vektorů v konstantním čase:
// 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
}