Sdílet prostřednictvím


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_iteratorto, že místo toho nejde použít output_iterator , protože nejde napsat.

Diagram hierarchie iterátoru input_or_output_iterator je základ. input_iterator a output_iterator se zobrazují jako upřesňující input_or_output_iterator. forward_iterator je další a upřesňuje input_iterator i output_iterator. bidirectional_iterator upřesňuje forward_iterator. random_access_iterator upřesňuje bidirectional_iterator. Nakonec contiguous_iterator upřesňuje random_access_iterator

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_iteratorC++20 Základem taxonomie konceptu iterátoru. Dopředu Čtení/zápis ne istream_iterator, ostream_iterator
output_iteratorC++20 Určuje iterátor, do kterého můžete zapisovat. Dopředu Write ne ostream, inserter
input_iteratorC++20 Určuje iterátor, ze kterého můžete číst jednou. Dopředu Čteno ne istream, istreambuf_iterator
forward_iteratorC++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_iteratorC++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_iteratorC++20 Určuje iterátor, který můžete číst a zapisovat podle indexu. Dopředu nebo dozadu Čtení/zápis ano vector, , arraydeque
contiguous_iteratorC++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_forC++20 Určuje, že typ je sentinel pro typ iterátoru.
sized_sentinel_forC++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>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, vectora string.

Příklad: contiguous_iterator

Následující příklad používá contiguous_iterator koncept, který ukazuje, že vector<int>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>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_iteratorinput_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>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>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_iteratora bidirectional_iterator.

Některé příklady random_access_iterator jsou vector, arraya deque.

Příklad: random_access_iterator

Následující příklad ukazuje, že vector<int>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_iteratora 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
}    

Viz také

Koncepty rozsahu
Adaptéry rozsahu
Zobrazit třídy