Share via


<ranges> Konsep

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 pengalihan /diagnostics:caret pengkompilasi 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 rentang didefinisikan dalam std::ranges namespace, dan dideklarasikan dalam <ranges> file header. Mereka digunakan dalam deklarasi adaptor rentang, tampilan, dan sebagainya.

Ada enam kategori rentang. Mereka terkait dengan kategori iterator yang tercantum dalam <iterator> konsep. Untuk meningkatkan kemampuan, kategorinya adalah:

Konsep rentang Deskripsi
output_range
input_range
Menentukan rentang yang bisa Anda tulis.
Menentukan rentang yang bisa Anda baca sekali.
forward_range Menentukan rentang yang dapat Anda baca (dan mungkin menulis) beberapa kali.
bidirectional_range Menentukan rentang yang dapat Anda baca dan tulis maju dan mundur.
random_access_range Menentukan rentang yang dapat Anda baca dan tulis berdasarkan indeks.
contiguous_range Menentukan rentang yang elemennya berurutan dalam memori, berukuran sama, dan dapat diakses menggunakan aritmatika pointer.

Dalam tabel sebelumnya, konsep tercantum dalam urutan peningkatan kemampuan. Rentang yang memenuhi persyaratan konsep umumnya memenuhi persyaratan konsep dalam baris yang mendahuluinya. Misalnya, memiliki random_access_range kemampuan bidirectional_range, , forward_range, input_rangedan output_range. Pengecualiannya adalah input_range, yang tidak dapat ditulis, sehingga tidak memiliki kemampuan output_range.

Diagram of the ranges iterator hierarchy. input_range and output_range are the most basic iterators. forward_range is next and refines both input_range and output_range. bidirectional_range refines forward_range. random_access_range refines bidirectional_range. Finally, contiguous_range refines random_access_range

Konsep rentang lainnya meliputi:

Konsep rentang Deskripsi
rangeC++20 Menentukan jenis yang menyediakan iterator dan sentinel.
borrowed_rangeC++20 Menentukan bahwa masa pakai iterator rentang tidak terkait dengan masa pakai rentang.
common_rangeC++20 Menentukan bahwa jenis iterator rentang dan jenis sentinel rentang sama.
Simple_ViewC++20 Bukan konsep resmi yang didefinisikan sebagai bagian dari pustaka standar, tetapi digunakan sebagai konsep pembantu pada beberapa antarmuka.
sized_rangeC++20 Menentukan rentang yang dapat menyediakan jumlah elemennya secara efisien.
viewC++20 Menentukan jenis yang memiliki konstruksi pemindahan, penugasan, dan penghancuran yang efisien (waktu konstan).
viewable_rangeC++20 Menentukan tipe yang merupakan tampilan atau dapat dikonversi menjadi satu.

bidirectional_range

Mendukung bidirectional_range membaca dan menulis rentang maju dan mundur.

template<class T>
concept bidirectional_range =
    forward_range<T> && bidirectional_iterator<iterator_t<T>>;

Parameter

T
Jenis yang akan diuji untuk melihat apakah itu adalah bidirectional_range.

Keterangan

Rentang semacam ini mendukung bidirectional_iterator atau lebih besar. A bidirectional_iterator memiliki kemampuan forward_iterator, tetapi juga dapat melakukan iterasi mundur.

Beberapa contoh bidirectional_range adalah std::set, , std::vectordan std::list.

borrowed_range

Model borrowed_range jenis jika validitas iterator yang Anda dapatkan dari objek dapat lebih lama dari masa pakai objek. Artinya, iterator untuk rentang dapat digunakan bahkan ketika rentang tidak ada lagi.

template<class T>
concept borrowed_range =
    range<T> &&
    (is_lvalue_reference_v<T> || enable_borrowed_range<remove_cvref_t<T>>);

Parameter

T
Jenis yang akan diuji untuk melihat apakah itu adalah borrowed_range.

Keterangan

Masa pakai rentang rvalue dapat berakhir mengikuti panggilan fungsi apakah model borrowed_range rentang atau tidak. Jika itu adalah borrowed_range, Anda mungkin dapat terus menggunakan iterator dengan perilaku yang ditentukan dengan baik terlepas dari kapan masa pakai rentang berakhir.

Kasus di mana ini tidak benar adalah, misalnya, untuk kontainer seperti vector atau list karena ketika masa pakai kontainer berakhir, iterator akan merujuk ke elemen yang telah dihancurkan.

Anda dapat terus menggunakan iterator untuk borrowed_range, misalnya, untuk view seperti iota_view<int>{0, 42} yang iteratornya melebihi set nilai yang tidak tunduk pada dihancurkan karena dibuat sesuai permintaan.

common_range

Jenis iterator untuk sama common_range dengan jenis sentinel. Yaitu, begin() dan end() mengembalikan jenis yang sama.

template<class T>
concept common_range =
   ranges::range<T> && std::same_as<ranges::iterator_t<T>, ranges::sentinel_t<T>>;

Parameter

T
Jenis yang akan diuji untuk melihat apakah itu adalah common_range.

Keterangan

Mendapatkan jenis dari std::ranges::begin() dan std::ranges::end() penting untuk algoritma yang menghitung jarak antara dua iterator, dan untuk algoritma yang menerima rentang yang ditandai oleh pasangan iterator.

Kontainer standar (misalnya, vector) memenuhi persyaratan common_range.

contiguous_range

Elemen contiguous_range disimpan secara berurutan dalam memori dan dapat diakses menggunakan aritmatika pointer. Misalnya, array adalah contiguous_range.

template<class T>
concept contiguous_range =
    random_access_range<T> && contiguous_iterator<iterator_t<T>> &&
    requires(T& t) {{ ranges::data(t) } -> same_as<add_pointer_t<range_reference_t<T>>>;};

Parameter

T
Jenis yang akan diuji untuk melihat apakah itu adalah contiguous_range.

Keterangan

contiguous_range Dapat diakses oleh aritmatika pointer karena elemen ditata secara berurutan dalam memori dan berukuran sama. Rentang semacam ini mendukung continguous_iterator, yang merupakan yang paling fleksibel dari semua iterator.

Beberapa contoh contiguous_range adalah std::array, , std::vectordan std::string.

Contoh: contiguous_range

Contoh berikut menunjukkan penggunaan aritmatika penunjuk untuk mengakses contiguous_range:

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

int main()
{
    // Show that vector is a contiguous_range
    std::vector<int> v = {0,1,2,3,4,5};
    std::cout << std::boolalpha << std::ranges::contiguous_range<decltype(v)> << '\n'; // outputs true

    // Show that pointer arithmetic can be used to access the elements of a contiguous_range
    auto ptr = v.data();
    ptr += 2;
    std::cout << *ptr << '\n'; // outputs 2
}
true
2

forward_range

Mendukung forward_range pembacaan (dan mungkin menulis) rentang beberapa kali.

template<class T>
concept forward_range = input_range<T> && forward_iterator<iterator_t<T>>;

Parameter

T
Jenis yang akan diuji untuk melihat apakah itu adalah forward_range.

Keterangan

Rentang semacam ini mendukung forward_iterator atau lebih besar. Dapat forward_iterator melakukan iterasi dalam rentang beberapa kali.

input_range

Adalah input_range rentang yang dapat dibaca dari sekali.

template<class T>
concept input_range = range<T> && input_iterator<iterator_t<T>>;

Parameter

T
Jenis yang akan diuji untuk melihat apakah itu adalah input_range.

Keterangan

Ketika jenis memenuhi persyaratan input_range:

  • Fungsi ranges::begin() mengembalikan input_iterator. Memanggil begin() lebih dari sekali pada hasil dalam perilaku yang input_range tidak ditentukan.
  • Anda dapat mendereferensikan input_iterator berulang kali, yang menghasilkan nilai yang sama setiap kali. input_range Bukan multi-pass. Meningkatkan iterator membatalkan salinan apa pun.
  • Ini dapat digunakan dengan ranges::for_each.
  • Ini mendukung input_iterator atau lebih besar.

output_range

Adalah output_range rentang yang dapat Anda tulis.

template<class R, class T>
concept output_range = range<R> && output_iterator<iterator_t<R>, T>;

Parameter

R
Jenis rentang.

T
Jenis data yang akan ditulis ke rentang.

Keterangan

Arti dari output_iterator<iterator_t<R>, T> adalah bahwa jenis menyediakan iterator yang dapat menulis nilai jenis T ke rentang jenis R. Dengan kata lain, mendukung output_iterator atau lebih besar.

random_access_range

Dapat random_access_range membaca atau menulis rentang menurut indeks.

template<class T>
concept random_access_range =
bidirectional_range<T> && random_access_iterator<iterator_t<T>>;

Parameter

T
Jenis yang akan diuji untuk melihat apakah itu adalah sized_range.

Keterangan

Rentang semacam ini mendukung random_access_iterator atau lebih besar. A random_access_range memiliki kemampuan input_range, , output_range, forward_rangedan bidirectional_range. A random_access_range dapat diurutkan.

Beberapa contoh random_access_range adalah std::vector, , std::arraydan std::deque.

range

Mendefinisikan persyaratan yang harus dipenuhi jenis menjadi range. menyediakan range iterator dan sentinel, sehingga Anda dapat melakukan iterasi di atas elemennya.

template<class T>
concept range = requires(T& rg)
{
  ranges::begin(rg);
  ranges::end(rg);
};

Parameter

T
Jenis yang akan diuji untuk melihat apakah itu adalah range.

Keterangan

Persyaratannya range adalah:

  • Ini dapat diulang menggunakan std::ranges::begin() dan std::ranges::end()
  • ranges::begin() dan ranges::end() jalankan dalam waktu konstanta yang diamortisasi dan jangan ubah range. Waktu konstanta yang diamortisasi tidak berarti O(1), tetapi bahwa biaya rata-rata atas serangkaian panggilan, bahkan dalam kasus terburuk, adalah O(n) daripada O(n^2) atau lebih buruk.
  • [ranges::begin(), ranges::end()) menunjukkan rentang yang valid.

Simple_View

adalah Simple_View konsep khusus eksposisi yang digunakan pada beberapa ranges antarmuka. Ini tidak didefinisikan dalam pustaka. Ini hanya digunakan dalam spesifikasi untuk membantu menjelaskan perilaku beberapa adaptor rentang.

template<class V>
  concept Simple_View = // exposition only
    ranges::view<V> && ranges::range<const V> &&
    std::same_as<std::ranges::iterator_t<V>, std::ranges::iterator_t<const V>> &&
    std::same_as<std::ranges::sentinel_t<V>, std::ranges::sentinel_t<const V>>;

Parameter

V
Jenis yang akan diuji untuk melihat apakah itu adalah Simple_View.

Keterangan

Tampilan V adalah Simple_View jika semua hal berikut ini benar:

  • V adalah tampilan
  • const V adalah rentang
  • Keduanya v dan const V memiliki jenis iterator dan sentinel yang sama.

sized_range

A sized_range menyediakan jumlah elemen dalam rentang dalam waktu konstanta yang diamortisasi.

template<class T>
  concept sized_range = range<T> &&
    requires(T& t) { ranges::size(t); };

Parameter

T
Jenis yang akan diuji untuk melihat apakah itu adalah sized_range.

Keterangan

Persyaratan dari sized_range adalah bahwa memanggil ranges::size di atasnya:

  • Tidak mengubah rentang.
  • Mengembalikan jumlah elemen dalam waktu konstanta yang diamortisasi. Waktu konstanta yang diamortisasi tidak berarti O(1), tetapi bahwa biaya rata-rata atas serangkaian panggilan, bahkan dalam kasus terburuk, adalah O(n) daripada O(n^2) atau lebih buruk.

Beberapa contoh adalah sized_rangestd::list dan std::vector.

Contoh: sized_range

Contoh berikut menunjukkan bahwa salah int satunya vector adalah sized_range:

// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>

int main()
{
    std::cout << std::boolalpha << std::ranges::sized_range<std::vector<int>> << '\n'; // outputs "true"
}    

view

Memiliki view konstruksi pemindahan waktu yang konstan, penugasan, dan operasi penghancuran --terlepas dari jumlah elemen yang dimilikinya. Tampilan tidak perlu disalin yang dapat dibuat atau disalin yang dapat ditetapkan, tetapi jika ya, operasi tersebut juga harus berjalan dalam waktu konstan.

Karena persyaratan waktu yang konstan, Anda dapat menyusun tampilan secara efisien. Misalnya, mengingat vektor yang int disebut input, fungsi yang menentukan apakah angka dapat dibagi tiga, dan fungsi yang mengkuadratkan angka, pernyataan auto x = input | std::views::filter(divisible_by_three) | std::views::transform(square); secara efisien menghasilkan tampilan yang berisi kuadrat angka dalam input yang dapat dibagi tiga. Koneksi tampilan bersama-sama | disebut sebagai menyusun tampilan. Jika jenis memenuhi view konsep, maka jenis tersebut dapat disusam secara efisien.

template<class T>
concept view = ranges::range<T> && std::movable<T> && ranges::enable_view<T>;

Parameter

T
Jenis yang akan diuji untuk melihat apakah itu tampilan.

Keterangan

Persyaratan penting yang membuat tampilan dapat dikompositasikan adalah murah untuk dipindahkan/disalin. Ini karena tampilan dipindahkan/disalin saat disusam dengan tampilan lain. Ini harus rentang bergerak.

ranges::enable_view<T> adalah sifat yang digunakan untuk mengklaim kesesuaian view dengan persyaratan semantik konsep. Jenis dapat memilih ikut serta dengan:

  • secara publik dan tidak ambigu yang berasal dari spesialisasi ranges::view_interface
  • secara publik dan tidak ambigu yang berasal dari kelas ranges::view_basekosong , atau
  • mengkhususkan diri ranges::enable_view<T> untuk true

Opsi 1 lebih disukai karena view_interface juga menyediakan implementasi default yang menyimpan beberapa kode boilerplate yang harus Anda tulis.

Gagal itu, opsi 2 sedikit lebih sederhana daripada opsi 3.

Keuntungan dari opsi 3 adalah mungkin tanpa mengubah definisi jenis.

viewable_range

viewable_range adalah jenis yang merupakan tampilan atau dapat dikonversi menjadi satu.

template<class T>
  concept viewable_range =
    range<T> && (borrowed_range<T> || view<remove_cvref_t<T>>);

Parameter

T
Jenis yang akan diuji untuk melihat apakah itu adalah tampilan atau dapat dikonversi menjadi tampilan.

Keterangan

Gunakan std::ranges::views::all() untuk mengonversi rentang menjadi tampilan.

Baca juga

<ranges>
Adaptor rentang
Melihat kelas