<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_range
dan output_range
. Pengecualiannya adalah input_range
, yang tidak dapat ditulis, sehingga tidak memiliki kemampuan output_range
.
Konsep rentang lainnya meliputi:
Konsep rentang | Deskripsi |
---|---|
range C++20 |
Menentukan jenis yang menyediakan iterator dan sentinel. |
borrowed_range C++20 |
Menentukan bahwa masa pakai iterator rentang tidak terkait dengan masa pakai rentang. |
common_range C++20 |
Menentukan bahwa jenis iterator rentang dan jenis sentinel rentang sama. |
Simple_View C++20 |
Bukan konsep resmi yang didefinisikan sebagai bagian dari pustaka standar, tetapi digunakan sebagai konsep pembantu pada beberapa antarmuka. |
sized_range C++20 |
Menentukan rentang yang dapat menyediakan jumlah elemennya secara efisien. |
view C++20 |
Menentukan jenis yang memiliki konstruksi pemindahan, penugasan, dan penghancuran yang efisien (waktu konstan). |
viewable_range C++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::vector
dan 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::vector
dan 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()
mengembalikaninput_iterator
. Memanggilbegin()
lebih dari sekali pada hasil dalam perilaku yanginput_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_range
dan bidirectional_range
. A random_access_range
dapat diurutkan.
Beberapa contoh random_access_range
adalah std::vector
, , std::array
dan 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()
danstd::ranges::end()
ranges::begin()
danranges::end()
jalankan dalam waktu konstanta yang diamortisasi dan jangan ubahrange
. 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 tampilanconst V
adalah rentang- Keduanya
v
danconst 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_range
std::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_base
kosong , atau - mengkhususkan diri
ranges::enable_view<T>
untuktrue
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
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk