Bagikan melalui


Konsep iterator

Konsep adalah fitur bahasa C++20 yang membatasi parameter templat pada waktu kompilasi. Ini membantu mencegah instansiasi templat yang salah, menentukan persyaratan argumen templat dalam bentuk yang dapat dibaca, dan memberikan kesalahan kompilator terkait templat yang lebih ringkas.

Pertimbangkan contoh berikut, yang menentukan konsep untuk mencegah pembuatan instans templat dengan jenis yang tidak mendukung pembagian:

// 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 
}

Saat Anda meneruskan sakelar /diagnostics:caret kompilator ke Visual Studio 2022 versi 17.4 pratinjau 4 atau yang lebih baru, kesalahan yang dievaluasi konsep dividable<char*> ke false akan mengarah langsung ke persyaratan (a / b) ekspresi yang gagal.

Konsep iterator didefinisikan dalam std namespace layanan, dan dideklarasikan dalam <iterator> file header. Mereka digunakan dalam deklarasi adaptor rentang, tampilan, dan sebagainya.

Ada enam kategori iterator. Mereka terkait langsung dengan kategori rentang yang tercantum di bawah Konsep rentang.

Konsep iterator berikut tercantum dalam urutan peningkatan kemampuan. input_or_output_iterator berada di ujung rendah hierarki kemampuan, dan contiguous_iterator berada di ujung atas. Iterator yang lebih tinggi dalam hierarki umumnya dapat digunakan sebagai pengganti yang lebih rendah, tetapi tidak sebaliknya. Misalnya, iterator random_access_iterator dapat digunakan sebagai pengganti forward_iterator, tetapi bukan sebaliknya. Pengecualiannya adalah input_iterator, yang tidak dapat digunakan karena output_iterator tidak dapat menulis.

Diagram hierarki iterator. input_or_output_iterator adalah dasarnya. input_iterator dan output_iterator ditampilkan sebagai input_or_output_iterator penyempurnaan. forward_iterator berikutnya dan menyempurnakan input_iterator dan output_iterator. bidirectional_iterator menyempurnakan forward_iterator. random_access_iterator menyempurnakan bidirectional_iterator. Akhirnya, contiguous_iterator menyempurnakan random_access_iterator

Dalam tabel berikut, "Multi-pass" mengacu pada apakah iterator dapat mengunjungi kembali elemen yang sama lebih dari sekali. Misalnya, vector::iterator adalah iterator multi-pass karena Anda dapat membuat salinan iterator, membaca elemen dalam koleksi, lalu memulihkan iterator ke nilai dalam salinan, dan mengunjungi kembali elemen yang sama lagi. Jika iterator adalah single-pass, Anda hanya dapat mengunjungi elemen dalam koleksi sekali.

Dalam tabel berikut, "Jenis contoh" mengacu pada koleksi/iterator yang memenuhi konsep.

Konsep iterator Deskripsi Petunjuk Baca/tulis Multi-pass Contoh jenis
input_or_output_iterator C++20 Dasar taksonomi konsep iterator. Selanjutnya Baca/tulis Tidak istream_iterator, ostream_iterator
output_iterator C++20 Menentukan iterator yang dapat Anda tulis. Selanjutnya Menulis Tidak ostream, inserter
input_iterator C++20 Menentukan iterator yang dapat Anda baca sekali. Selanjutnya Bacalah Tidak istream, istreambuf_iterator
forward_iterator C++20 Menentukan iterator yang dapat membaca (dan mungkin menulis) beberapa kali. Selanjutnya Baca/tulis Ya vector, list
bidirectional_iterator C++20 Menentukan iterator yang dapat Anda baca dan tulis teruskan dan mundur. Maju atau mundur Baca/tulis Ya list, , setmultiset, map, dan multimap.
random_access_iterator C++20 Menentukan iterator yang dapat Anda baca dan tulis menurut indeks. Maju atau mundur Baca/tulis Ya vector, , arraydeque
contiguous_iterator C++20 Menentukan iterator yang elemennya berurutan dalam memori, berukuran sama, dan dapat diakses menggunakan aritmatika penunjuk. Maju atau mundur Baca/tulis Ya array, , vector. string

Konsep iterator lainnya meliputi:

Konsep iterator Deskripsi
sentinel_for C++20 Menentukan bahwa jenis adalah sentinel untuk jenis iterator.
sized_sentinel_for C++20 Menentukan bahwa iterator dan sentinelnya dapat dikurangi (menggunakan -) untuk menemukan perbedaannya dalam waktu konstan.

bidirectional_iterator

Mendukung bidirectional_iterator pembacaan dan penulisan maju dan mundur.

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>;
};

Parameter

I
Iterator untuk menguji untuk melihat apakah itu adalah bidirectional_iterator.

Keterangan

A bidirectional_iterator memiliki kemampuan forward_iterator, tetapi juga dapat melakukan iterasi mundur.

Beberapa contoh kontainer yang dapat digunakan dengan bidirectional_iterator adalah , , set, multisetmap, multimap, dan vectorlist.

Contoh: bidirectional_iterator

Contoh berikut menggunakan bidirectional_iterator konsep untuk menunjukkan yang vector<int> memiliki 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

Menentukan iterator yang elemennya berurutan dalam memori, berukuran sama, dan dapat diakses menggunakan aritmatika penunjuk.

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>>>;
        };

Parameter

I
Jenis yang akan diuji untuk melihat apakah itu adalah contiguous_iterator.

Keterangan

contiguous_iterator Dapat diakses oleh aritmatika pointer karena elemen ditata secara berurutan dalam memori dan berukuran sama. Beberapa contoh contiguous_iterator adalah array, , vectordan string.

Contoh: contiguous_iterator

Contoh berikut menggunakan contiguous_iterator konsep untuk menunjukkan bahwa memiliki 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

Memiliki kemampuan dan input_iteratoroutput_iterator. Mendukung iterasi selama koleksi beberapa kali.

template<class I>
    concept forward_iterator =
        input_iterator<I> &&
        derived_from<ITER_CONCEPT(I), forward_iterator_tag> &&
        incrementable<I> &&
        sentinel_for<I, I>;

Parameter

I
Iterator untuk menguji untuk melihat apakah itu adalah forward_iterator.

Keterangan

A forward_iterator hanya dapat bergerak maju.

Beberapa contoh kontainer yang dapat digunakan dengan forward_iterator adalah , , vector, listunordered_set, unordered_multiset, dan unordered_mapunordered_multimap.

Contoh: forward_iterator

Contoh berikut menggunakan forward_iterator konsep untuk menunjukkan bahwa memiliki 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

input_iterator adalah iterator yang dapat Anda baca dari setidaknya sekali.

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>;

Parameter

I
Jenis yang akan diuji untuk melihat apakah itu adalah input_iterator.

Keterangan

Memanggil begin() pada input_iterator lebih dari sekali menghasilkan perilaku yang tidak ditentukan. Jenis yang hanya model input_iterator tidak multi-pass. Pertimbangkan untuk membaca dari input standar (cin) misalnya. Dalam hal ini, Anda hanya dapat membaca elemen saat ini sekali dan Anda tidak dapat membaca ulang karakter yang telah Anda baca. Satu-satunya input_iterator bacaan ke depan, bukan mundur.

Contoh: input_iterator

Contoh berikut menggunakan input_iterator konsep untuk menunjukkan bahwa memiliki 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

Adalah input_or_output_iterator dasar dari taksonomi konsep iterator. Ini mendukung dereferensi dan inkrementasi iterator. Setiap model input_or_output_iteratoriterator .

template<class I>
concept input_or_output_iterator =
    requires(I i) {
        { *i } -> can-reference;
    } &&
    weakly_incrementable<I>;

Parameter

I
Jenis yang akan diuji untuk melihat apakah itu adalah input_or_output_iterator.

Keterangan

Konsepnya can-reference berarti bahwa jenis I tersebut adalah referensi, penunjuk, atau jenis yang dapat dikonversi secara implisit menjadi referensi.

Contoh: input_or_output_iterator

Contoh berikut menggunakan input_or_output_iterator konsep untuk menunjukkan yang vector<int> memiliki 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 Adalah iterator yang dapat Anda tulis.

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);
    };

Parameter

I
Jenis yang akan diuji untuk melihat apakah itu adalah output_iterator.

T
Jenis nilai yang akan ditulis.

Keterangan

Sebuah output_iterator adalah single pass. Artinya, ia hanya dapat menulis ke elemen yang sama sekali.

Contoh: output_iterator

Contoh berikut menggunakan output_iterator konsep untuk menunjukkan yang vector<int> memiliki 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

Dapat random_access_iterator membaca atau menulis berdasarkan indeks.

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>>;
    };

Parameter

I
Jenis yang akan diuji untuk melihat apakah itu adalah random_access_iterator.

Keterangan

A random_access_iterator memiliki kemampuan input_iterator, , output_iterator, forward_iteratordan bidirectional_iterator.

Beberapa contoh random_access_iterator adalah vector, , arraydan deque.

Contoh: random_access_iterator

Contoh berikut menunjukkan bahwa memiliki 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

Menentukan bahwa jenis adalah sentinel untuk iterator.

template<class S, class I>
concept sentinel_for =
    semiregular<S> &&
    input_or_output_iterator<I> &&
    weakly-equality-comparable-with <S, I>;

Parameter

I
Jenis iterator.

S
Jenis yang akan diuji untuk melihat apakah itu sentinel untuk I.

Keterangan

Sentinel adalah jenis yang dapat dibandingkan dengan iterator untuk menentukan apakah iterator telah mencapai akhir. Konsep ini menentukan apakah jenis adalah sentinel untuk salah satu jenisinput_or_output_iterator, yang mencakup input_iterator, , output_iterator, forward_iteratorbidirectional_iterator, random_access_iterator, dan contiguous_iterator.

Contoh: sentinel_for

Contoh berikut menggunakan sentinel_for konsep untuk menunjukkan bahwa vector<int>::iterator adalah sentinel untuk 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

Uji bahwa iterator dan sentinelnya dapat dikurangi menggunakan - untuk menemukan perbedaannya, dalam waktu konstan.

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>>;
    };

Parameter

I
Jenis iterator.

S
Jenis sentinel untuk diuji.

Contoh: sized_sentinel_for

Contoh berikut menggunakan sized_sentinel_for konsep untuk memverifikasi bahwa sentinel untuk vector<int> dapat dikurangi dari iterator vektor dalam waktu konstan:

// 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
}    

Lihat juga

Konsep rentang
Adaptor rentang
Melihat kelas