Pertemuan di Cologne telah berlalu, standar C ++ 20 telah direduksi menjadi lebih atau kurang jadi (setidaknya sampai munculnya catatan khusus), dan saya ingin berbicara tentang salah satu inovasi yang akan datang. Ini adalah mekanisme yang biasanya disebut
operator <=> (standar mendefinisikannya sebagai "operator perbandingan tiga arah", tetapi memiliki julukan informal "pesawat ruang angkasa"), tetapi saya percaya bahwa cakupannya jauh lebih luas.
Kami tidak hanya akan memiliki operator baru - semantik perbandingan akan mengalami perubahan signifikan pada tingkat bahasa itu sendiri.
Bahkan jika Anda tidak dapat mengeluarkan hal lain dari artikel ini, ingatlah tabel ini:
Sekarang kita akan memiliki operator baru,
<=> , tetapi, yang lebih penting, operator sekarang sistematis. Ada operator dasar dan ada operator turunan - setiap grup memiliki kemampuannya sendiri.
Kami akan berbicara tentang fitur-fitur ini secara singkat dalam pendahuluan dan mempertimbangkan lebih detail di bagian berikut.
Operator dasar dapat
dibalik (mis. Ditulis ulang dengan urutan parameter terbalik). Pernyataan yang diturunkan dapat
ditulis ulang melalui pernyataan dasar yang sesuai. Baik kandidat yang dikonversi atau ditulis ulang tidak menghasilkan fungsi baru, mereka hanya penggantian pada tingkat kode sumber dan dipilih dari
serangkaian kandidat yang diperluas . Sebagai contoh, ekspresi
a <9 sekarang dapat dievaluasi sebagai
operator. <=> (9) <0 , dan ekspresi
10! = B sebagai
! Operator == (b, 10) . Ini berarti bahwa akan dimungkinkan untuk membuang satu atau dua operator di mana, untuk mencapai perilaku yang sama, sekarang diperlukan untuk menulis 2, 4, 6, atau bahkan 12 operator secara manual. Gambaran
singkat dari aturan akan disajikan di bawah ini bersama dengan tabel dari semua transformasi yang mungkin.
Baik operator dasar dan derivatif dapat didefinisikan sebagai
standar . Dalam hal operator dasar, ini berarti bahwa operator akan diterapkan pada setiap anggota dalam urutan deklarasi; dalam hal operator turunan, kandidat yang ditulis ulang akan digunakan.
Perlu dicatat bahwa tidak ada transformasi semacam itu di mana operator dari satu jenis (mis., Kesetaraan atau pemesanan) dapat dinyatakan dalam istilah operator dari jenis lain. Dengan kata lain, kolom dalam tabel kami sama sekali tidak bergantung satu sama lain. Ekspresi
a == b tidak akan pernah dievaluasi sebagai
operator <=> (a, b) == 0 secara implisit (tapi, tentu saja, tidak ada yang menghalangi Anda untuk mendefinisikan
operator Anda
== menggunakan
operator <=> jika Anda mau).
Pertimbangkan contoh kecil di mana kami menunjukkan bagaimana kode terlihat sebelum dan sesudah menerapkan fungsi baru. Kita akan menulis tipe string yang tidak case-sensitive,
CIString , yang objeknya dapat dibandingkan satu sama lain dan dengan
char const * .
Dalam C ++ 17, untuk tugas kita, kita perlu menulis 18 fungsi perbandingan:
class CIString { string s; public: friend bool operator==(const CIString& a, const CIString& b) { return assize() == bssize() && ci_compare(asc_str(), bsc_str()) == 0; } friend bool operator< (const CIString& a, const CIString& b) { return ci_compare(asc_str(), bsc_str()) < 0; } friend bool operator!=(const CIString& a, const CIString& b) { return !(a == b); } friend bool operator> (const CIString& a, const CIString& b) { return b < a; } friend bool operator>=(const CIString& a, const CIString& b) { return !(a < b); } friend bool operator<=(const CIString& a, const CIString& b) { return !(b < a); } friend bool operator==(const CIString& a, const char* b) { return ci_compare(asc_str(), b) == 0; } friend bool operator< (const CIString& a, const char* b) { return ci_compare(asc_str(), b) < 0; } friend bool operator!=(const CIString& a, const char* b) { return !(a == b); } friend bool operator> (const CIString& a, const char* b) { return b < a; } friend bool operator>=(const CIString& a, const char* b) { return !(a < b); } friend bool operator<=(const CIString& a, const char* b) { return !(b < a); } friend bool operator==(const char* a, const CIString& b) { return ci_compare(a, bsc_str()) == 0; } friend bool operator< (const char* a, const CIString& b) { return ci_compare(a, bsc_str()) < 0; } friend bool operator!=(const char* a, const CIString& b) { return !(a == b); } friend bool operator> (const char* a, const CIString& b) { return b < a; } friend bool operator>=(const char* a, const CIString& b) { return !(a < b); } friend bool operator<=(const char* a, const CIString& b) { return !(b < a); } };
Di C ++ 20, Anda dapat melakukan hanya 4 fungsi:
class CIString { string s; public: bool operator==(const CIString& b) const { return s.size() == bssize() && ci_compare(s.c_str(), bsc_str()) == 0; } std::weak_ordering operator<=>(const CIString& b) const { return ci_compare(s.c_str(), bsc_str()) <=> 0; } bool operator==(char const* b) const { return ci_compare(s.c_str(), b) == 0; } std::weak_ordering operator<=>(const char* b) const { return ci_compare(s.c_str(), b) <=> 0; } };
Saya akan memberi tahu Anda apa artinya semua ini, secara lebih rinci, tetapi pertama-tama, mari kembali sedikit dan ingat bagaimana perbandingan bekerja hingga standar C ++ 20.
Perbandingan dalam Standar dari C ++ 98 ke C ++ 17
Operasi perbandingan tidak banyak berubah sejak penciptaan bahasa. Kami memiliki enam operator:
== ,! = ,
< ,
> ,
<= Dan
> = . Standar mendefinisikan masing-masing untuk tipe bawaan, tetapi secara umum mereka mematuhi aturan yang sama. Ketika mengevaluasi ekspresi
a @ b (di mana
@ adalah salah satu dari enam operator perbandingan), kompiler mencari fungsi anggota, fungsi bebas, dan kandidat bawaan bernama
operator @ , yang dapat dipanggil dengan tipe
A atau
B dalam urutan yang ditentukan. Kandidat yang paling cocok dipilih dari mereka. Itu saja. Faktanya,
semua operator bekerja dengan cara yang sama: operasi
< tidak berbeda dari
<< .
Sekumpulan aturan sederhana seperti itu mudah dipelajari. Semua operator benar-benar independen dan setara. Tidak masalah apa yang kita manusia ketahui tentang hubungan mendasar antara
== dan
! = Operasi. Dari segi bahasa, ini satu dan sama. Kami menggunakan idiom. Sebagai contoh, kami mendefinisikan operator
! = Melalui
== :
bool operator==(A const&, A const&); bool operator!=(A const& lhs, A const& rhs) { return !(lhs == rhs); }
Demikian pula, melalui operator
< kami mendefinisikan semua operator hubungan lainnya. Kami menggunakan idiom-idiom ini karena, terlepas dari aturan bahasa, kami tidak benar-benar menganggap keenam operator itu setara. Kami menerima bahwa keduanya adalah dasar (
== dan
< ), dan melaluinya semua yang lain sudah dinyatakan.
Bahkan, Perpustakaan Templat Standar dibangun sepenuhnya di atas dua operator ini, dan sejumlah besar tipe dalam kode yang dieksploitasi mengandung definisi hanya satu dari mereka atau keduanya.
Namun, operator
< sangat tidak cocok untuk peran dasar karena dua alasan.
Pertama, operator hubungan lainnya tidak dapat dijamin untuk mengekspresikannya. Ya,
a> b berarti persis sama dengan
b <a , tetapi tidak benar bahwa
a <= b berarti persis sama dengan
! (B <a) . Dua ungkapan terakhir akan setara jika ada properti trikotomi, di mana untuk dua nilai apa pun hanya satu dari tiga pernyataan yang benar:
a <b ,
a == b atau
a> b . Jika ada trikotomi, ekspresi
a <= b berarti kita berurusan dengan kasus pertama atau kedua ... dan ini setara dengan pernyataan bahwa kita tidak berurusan dengan kasus ketiga. Karenanya
(a <= b) ==! (A> b) ==! (B <a) .
Tetapi bagaimana jika sikap tersebut tidak memiliki properti trikotomi? Ini adalah karakteristik dari hubungan urutan parsial. Contoh klasik adalah angka floating point yang salah satu dari operasi
1.f <NaN ,
1.f == NaN dan
1.f> NaN memberikan
false . Oleh karena itu,
1.f <= NaN juga memberikan
kebohongan , tetapi pada saat yang sama
! (NaN <1.f) benar .
Satu-satunya cara untuk mengimplementasikan operator
<= secara umum melalui operator dasar adalah mengecat kedua operasi sebagai
(a == b) || (a <b) , yang merupakan langkah besar ke belakang jika kita
masih harus berurusan dengan urutan linier, sejak saat itu tidak satu fungsi akan dipanggil, tetapi dua (misalnya, ungkapan
“abc..xyz9” <= “abc ..xyz1 " harus ditulis ulang sebagai
(" abc..xyz9 "==" abc..xyz1 ") || (" abc..xyz9 "<" abc..xyz1 ") dan dua kali untuk membandingkan seluruh baris).
Kedua, operator
<sangat tidak cocok untuk peran dasar karena kekhasan penggunaannya dalam perbandingan leksikografis. Pemrogram sering membuat kesalahan ini:
struct A { T t; U u; bool operator==(A const& rhs) const { return t == rhs.t && u == rhs.u; } bool operator< (A const& rhs) const { return t < rhs.t && u < rhs.u; } };
Untuk menentukan operator == untuk kumpulan elemen, cukup untuk menerapkan
== ke setiap anggota sekali, tetapi ini tidak akan berfungsi dengan
< operator. Dari sudut pandang implementasi ini, himpunan
A {1, 2} dan
A {2, 1} akan dianggap setara (karena tidak ada yang lebih rendah dari yang lain). Untuk memperbaikinya, terapkan operator
< dua kali untuk setiap anggota kecuali yang terakhir:
bool operator< (A const& rhs) const { if (t < rhs.t) return true; if (rhs.t < t) return false; return u < rhs.u; }
Akhirnya, untuk menjamin operasi yang benar dari perbandingan objek heterogen - yaitu untuk memastikan bahwa ekspresi
a == 10 dan
10 == a berarti hal yang sama - mereka biasanya merekomendasikan penulisan perbandingan sebagai fungsi bebas. Bahkan, ini biasanya satu-satunya cara untuk menerapkan perbandingan seperti itu. Ini tidak nyaman karena, pertama, Anda harus memantau kepatuhan dengan rekomendasi ini, dan kedua, Anda biasanya harus mendeklarasikan fungsi-fungsi seperti itu sebagai teman tersembunyi untuk implementasi yang lebih nyaman (mis. Di dalam tubuh kelas).
Perhatikan bahwa ketika membandingkan objek dari tipe yang berbeda tidak selalu perlu untuk menulis
operator == (X, int) ; mereka juga bisa berarti kasus di mana
int dapat dilemparkan ke
X secara implisit.
Mari kita meringkas aturan dengan standar C ++ 20:
- Semua pernyataan ditangani dengan cara yang sama.
- Kami menggunakan idiom untuk memfasilitasi implementasi. Operator == dan < kami menggunakan idiom dasar dan mengekspresikan operator hubungan yang tersisa melalui mereka.
- Itu hanya operator <sangat tidak cocok untuk peran pangkalan.
- Penting (dan disarankan) untuk menulis perbandingan objek heterogen sebagai fungsi bebas.
Operator pemesanan dasar baru: <=>
Perubahan paling signifikan dan nyata dalam pekerjaan perbandingan di C ++ 20 adalah penambahan operator baru -
operator <=> , operator perbandingan tiga arah.
Kita sudah terbiasa dengan perbandingan tiga arah dengan fungsi
memcmp /
strcmp di C dan
basic_string :: compare () di C ++. Mereka semua mengembalikan nilai tipe
int , yang diwakili oleh angka positif arbitrer jika argumen pertama lebih besar dari yang kedua,
0 jika mereka sama, dan angka negatif arbitrer sebaliknya.
Operator "pesawat ruang angkasa" tidak mengembalikan nilai
int , tetapi objek milik salah satu kategori perbandingan, yang nilainya mencerminkan jenis hubungan antara objek yang dibandingkan. Ada tiga kategori utama:
- strong_ordering : hubungan urutan linier di mana kesetaraan menyiratkan pertukaran elemen (mis. (a <=> b) == strong_ordering :: equal menyiratkan bahwa f (a) == f (b) berlaku untuk semua fungsi yang sesuai f Istilah "fungsi yang sesuai" sengaja tidak diberikan definisi yang jelas, tetapi ini tidak termasuk fungsi yang mengembalikan alamat argumen mereka atau kapasitas () dari vektor, dll. Kami hanya tertarik pada properti "esensial", yang juga sangat kabur, tetapi memungkinkan secara kondisional. menganggap bahwa kita berbicara tentang nilai tipe. Nilai vektor terkandung di dalamnya m elemen, tetapi bukan alamatnya, dll.). Kategori ini mencakup nilai-nilai berikut: strong_ordering :: lebih besar , strong_ordering :: sama dan kuat_ordering :: kurang .
- lemah_ordering : hubungan urutan linier di mana kesetaraan hanya mendefinisikan kelas ekivalen tertentu. Contoh klasiknya adalah perbandingan string tidak peka huruf besar-kecil, ketika dua objek bisa menjadi lemah_ordering :: setara , tetapi tidak sepenuhnya sama (ini menjelaskan penggantian kata sama dengan setara dalam nama nilai).
- partial_ordering : relasi urutan parsial. Dalam kategori ini, satu nilai lagi ditambahkan ke nilai-nilai yang lebih besar , setara, dan kurang (seperti pada lemah_ordering ) - tidak teratur ("tidak teratur"). Ini dapat digunakan untuk mengekspresikan hubungan urutan parsial dalam sistem tipe: 1.f <=> NaN memberikan nilai partial_ordering :: unordered .
Anda terutama akan bekerja dengan kategori
strong_ordering ; Ini juga merupakan kategori optimal untuk digunakan secara default. Sebagai contoh,
2 <=> 4 mengembalikan
strong_ordering :: kurang , dan
3 <=> -1 mengembalikan strong_ordering :: lebih besar .
Kategori-kategori dari tatanan yang lebih tinggi dapat secara implisit direduksi menjadi kategori-kategori dengan tatanan yang lebih lemah (mis.,
Strong_ordering dapat direduksikan menjadi
lemah_ordering ). Dalam kasus ini, tipe hubungan saat ini dipertahankan (mis.,
Strong_ordering :: equal berubah menjadi
lemah_ordering :: setara ).
Nilai-nilai kategori perbandingan dapat dibandingkan dengan literal
0 (tidak dengan
int apa pun dan tidak dengan
int sama dengan
0 , tetapi hanya dengan literal
0 ) menggunakan salah satu dari enam operator perbandingan:
strong_ordering::less < 0
Berkat perbandingan dengan
0 literal kita dapat mengimplementasikan operator relasi:
a @ b setara dengan
(a <=> b) @ 0 untuk masing-masing operator ini.
Sebagai contoh,
2 <4 dapat dihitung sebagai
(2 <=> 4) <0 , yang berubah menjadi
strong_ordering :: less <0 dan memberikan nilai
true .
Operator
<=> cocok dengan peran elemen dasar jauh lebih baik daripada operator
< , karena ia menghilangkan kedua masalah yang terakhir.
Pertama, ekspresi
a <= b dijamin setara dengan
(a <=> b) <= 0 bahkan dengan pemesanan parsial. Untuk dua nilai tidak berurutan,
a <=> b akan memberikan nilai
partial_ordered :: unordered , dan
partial_ordered :: unordered <= 0 akan memberikan
false , yang merupakan apa yang kita butuhkan. Ini dimungkinkan karena
<=> dapat mengembalikan lebih banyak varietas nilai: misalnya, kategori
partial_ordering berisi empat nilai yang mungkin. Nilai tipe
bool hanya bisa
benar atau
salah , jadi sebelum kita tidak bisa membedakan antara perbandingan nilai yang dipesan dan yang tidak teratur.
Untuk kejelasan, pertimbangkan contoh hubungan urutan parsial yang tidak terkait dengan angka floating point. Misalkan kita ingin menambahkan status NaN ke tipe
int , di mana NaN hanyalah nilai yang tidak membentuk pasangan yang dipesan dengan nilai apa pun yang terlibat. Anda dapat melakukan ini menggunakan
std :: opsional untuk menyimpannya:
struct IntNan { std::optional<int> val = std::nullopt; bool operator==(IntNan const& rhs) const { if (!val || !rhs.val) { return false; } return *val == *rhs.val; } partial_ordering operator<=>(IntNan const& rhs) const { if (!val || !rhs.val) {
Operator
<= mengembalikan nilai yang benar karena sekarang kita dapat mengekspresikan lebih banyak informasi pada level bahasa itu sendiri.
Kedua, untuk mendapatkan semua informasi yang diperlukan, cukup menerapkan
<=> sekali, yang memfasilitasi penerapan perbandingan leksikografis:
struct A { T t; U u; bool operator==(A const& rhs) const { return t == rhs.t && u == rhs.u; } strong_ordering operator<=>(A const& rhs) const {
Lihat
P0515 , kalimat asli untuk menambahkan
operator <=>, untuk diskusi yang lebih rinci
.Fitur operator baru
Kami tidak hanya mendapatkan operator baru. Pada akhirnya, jika contoh yang ditunjukkan di atas dengan deklarasi struktur
A hanya mengatakan bahwa alih-alih
x <y kita sekarang harus menulis
(x <=> y) <0 setiap kali, tidak ada yang akan menyukainya.
Mekanisme untuk menyelesaikan perbandingan dalam C ++ 20 berbeda nyata dari pendekatan lama, tetapi perubahan ini terkait langsung dengan konsep baru dua operator perbandingan dasar:
== dan
<=> . Jika sebelumnya itu adalah idiom (merekam melalui
== dan
< ), yang kami gunakan, tetapi yang tidak diketahui oleh kompiler, sekarang ia akan memahami perbedaan ini.
Sekali lagi, saya akan memberikan tabel yang sudah Anda lihat di awal artikel:
Setiap operator dasar dan turunan menerima kemampuan baru, yang akan saya ucapkan beberapa kata lebih lanjut.
Pembalikan operator dasar
Sebagai contoh, ambil tipe yang hanya bisa dibandingkan dengan
int :
struct A { int i; explicit A(int i) : i(i) { } bool operator==(int j) const { return i == j; } };
Dari sudut pandang aturan lama, tidak mengherankan bahwa ungkapan
a == 10 berfungsi dan dievaluasi menjadi
a.operator == (10) .
Tetapi bagaimana dengan
10 == a ? Dalam C ++ 17, ungkapan ini akan dianggap sebagai kesalahan sintaksis yang jelas. Tidak ada operator seperti itu. Agar kode tersebut dapat berfungsi, Anda harus menulis
operator simetris
== , yang pertama-tama akan mengambil nilai
int , dan kemudian
A ... dan untuk mengimplementasikannya harus dalam bentuk fungsi bebas.
Dalam C ++ 20, operator dasar dapat dibalik. Untuk
10 == a, kompiler akan menemukan kandidat
operator == (A, int) (sebenarnya, ini adalah fungsi anggota, tetapi untuk kejelasan, saya menuliskannya di sini sebagai fungsi bebas), dan kemudian tambahan - varian dengan urutan parameter terbalik, yaitu. .
operator == (int, A) . Kandidat kedua ini bertepatan dengan ekspresi kami (dan idealnya), jadi kami akan memilihnya. Ekspresi
10 == a dalam C ++ 20 dievaluasi sebagai
a.operator == (10) . Kompiler memahami bahwa kesetaraan adalah simetris.
Sekarang kami akan memperluas jenis kami sehingga dapat dibandingkan dengan
int tidak hanya melalui operator kesetaraan, tetapi juga melalui operator pemesanan:
struct A { int i; explicit A(int i) : i(i) { } bool operator==(int j) const { return i == j; } strong_ordering operator<=>(int j) const { return i <=> j; } };
Sekali lagi, ekspresi
a <=> 42 berfungsi dengan baik dan dihitung sesuai dengan aturan lama sebagai
operator. <=> (42) , tetapi
42 <=> a akan salah dari sudut pandang C ++ 17, bahkan jika operator
< => sudah ada dalam bahasa. Tetapi dalam C ++ 20,
operator <=> , seperti
operator == , simetris: ia mengenali kandidat yang terbalik. Untuk
42 <=> a,
operator fungsi anggota
<=> (A, int) akan ditemukan (sekali lagi, saya menulisnya di sini sebagai fungsi bebas hanya untuk kejelasan yang lebih besar), serta
operator kandidat sintetis
<=> (int, A) . Versi terbalik ini sama persis dengan ekspresi kami - kami memilihnya.
Namun,
42 <=> a TIDAK dihitung sebagai
operator. <=> (42) . Itu salah. Ungkapan ini dievaluasi menjadi
0 <=> a.operator <=> (42) . Coba cari tahu mengapa entri ini benar.
Penting untuk dicatat bahwa kompiler tidak membuat fungsi baru. Ketika menghitung
10 == a , operator
operator baru == (int, A) tidak muncul, dan ketika menghitung
42 <=> a ,
operator <=> (int, A) tidak muncul. Hanya dua ekspresi yang ditulis ulang melalui kandidat yang terbalik. Saya ulangi: tidak ada fungsi baru yang dibuat.
Juga perhatikan bahwa catatan dengan urutan parameter terbalik hanya tersedia untuk operator dasar, tetapi untuk turunannya tidak. Itu adalah:
struct B { bool operator!=(int) const; }; b != 42;
Penulisan Ulang Operator Turunan
Mari kita kembali ke contoh kita dengan struktur
A :
struct A { int i; explicit A(int i) : i(i) { } bool operator==(int j) const { return i == j; } strong_ordering operator<=>(int j) const { return i <=> j; } };
Ambil ungkapan
a! = 17 . Dalam C ++ 17, ini adalah kesalahan sintaksis karena
operator! = Operator tidak ada. Namun, dalam C ++ 20, untuk ekspresi yang mengandung operator perbandingan turunan, kompiler juga akan mencari operator dasar yang sesuai dan mengekspresikan perbandingan turunan melalui mereka.
Kita tahu bahwa dalam matematika, operasinya
! = Pada dasarnya berarti TIDAK
== . Sekarang ini diketahui oleh kompiler. Untuk ungkapan
a! = 17, ia tidak hanya akan mencari
operator! = Operator , tetapi juga
operator == (dan, seperti dalam contoh sebelumnya,
operator terbalik
== ). Untuk contoh ini, kami menemukan operator kesetaraan yang hampir cocok untuk kami - kami hanya perlu menulis ulang sesuai dengan semantik yang diinginkan:
a! = 17 akan dihitung sebagai
! (A == 17) .
Demikian pula,
17! = A dihitung sebagai
! A.operator == (17) , yang merupakan versi ditulis ulang dan versi terbalik.
Transformasi serupa juga dilakukan untuk memesan operator. Jika kami menulis
<9 , kami akan mencoba (tidak berhasil) untuk menemukan
operator < , dan juga mempertimbangkan kandidat dasar:
operator <=> . Penggantian yang sesuai untuk operator relasi terlihat seperti ini:
a @ b (di mana
@ adalah salah satu operator relasi) dihitung sebagai
(a <=> b) @ 0 . Dalam kasus kami,
a.operator <=> (9) <0 . Demikian pula,
9 <= a dihitung sebagai
0 <= a.operator <=> (9) .
Perhatikan bahwa, seperti dalam kasus panggilan, kompiler tidak membuat fungsi baru untuk kandidat yang ditulis ulang. Mereka hanya dihitung secara berbeda, dan semua transformasi dilakukan hanya pada tingkat kode sumber.
Di atas membawa saya ke saran berikut:
HANYA OPERATOR DASAR : Tetapkan hanya operator dasar (== dan <=>) dalam jenis Anda.Karena operator dasar memberikan seluruh rangkaian perbandingan, cukup untuk mendefinisikannya saja. Ini berarti bahwa Anda hanya perlu 2 operator untuk membandingkan objek dari tipe yang sama (bukan 6, seperti yang sekarang) dan hanya 2 operator untuk membandingkan berbagai jenis objek (bukan 12). Jika Anda hanya memerlukan operasi persamaan, maka cukup tulis 1 fungsi untuk membandingkan objek dari tipe yang sama (bukan 2) dan 1 fungsi untuk membandingkan berbagai jenis objek (bukan 4). Kelas
std :: sub_match adalah kasus ekstrem: di C ++ 17 menggunakan 42 operator perbandingan, dan di C ++ 20 hanya menggunakan 8, sedangkan fungsi tidak menderita sama sekali.
Karena kompiler juga mempertimbangkan kandidat terbalik, semua operator ini dapat diimplementasikan sebagai fungsi anggota. Anda tidak lagi harus menulis fungsi gratis hanya untuk membandingkan berbagai jenis objek.
Aturan khusus untuk menemukan kandidat
Seperti yang telah saya sebutkan, pencarian kandidat untuk
@ b di C ++ 17 dilakukan sesuai dengan prinsip berikut: kami menemukan semua
operator @ operator dan memilih yang paling cocok dari mereka.
C ++ 20 menggunakan serangkaian kandidat yang diperluas. Sekarang kita akan mencari semua
operator @ . Biarkan
@@ menjadi operator dasar untuk
@ (itu bisa menjadi operator yang sama). Kami juga menemukan semua
operator @@ dan untuk masing-masing kami tambahkan versinya yang terbalik. Dari semua kandidat yang ditemukan, kami memilih yang paling cocok.
Perhatikan bahwa overloading operator diizinkan dalam
sekali jalan. Kami tidak mencoba untuk menggantikan kandidat yang berbeda. Pertama kita kumpulkan semuanya, lalu pilih yang terbaik dari mereka. Jika ini tidak ada, pencarian, seperti sebelumnya, gagal.
Sekarang kami memiliki lebih banyak kandidat potensial, dan karenanya lebih banyak ketidakpastian. Perhatikan contoh berikut:
struct C { bool operator==(C const&) const; bool operator!=(C const&) const; }; bool check(C x, C y) { return x != y; }
Dalam C ++ 17, kami hanya memiliki satu kandidat untuk
x! = Y , dan sekarang ada tiga:
x.operator! = (Y) ,! X.operator == (y) dan
! Y.operator == (x) . Apa yang harus dipilih? Semuanya sama saja! (Catatan: kandidat
y.operator! = (X) tidak ada, karena hanya operator dasar yang dapat
dibalik .)
Dua aturan tambahan telah diperkenalkan untuk menghapus ketidakpastian ini. Kandidat yang belum bertobat lebih disukai daripada yang dikonversi; . ,
x.operator!=(y) «»
!x.operator==(y) , «»
!y.operator==(x) . , «» .
:
operator@@ . . , .
-. — (,
x < y , —
(x <=> y) < 0 ), (,
x <=> y void - , DSL), . . ,
bool ( :
operator== bool , ?)
Sebagai contoh:
struct Base { friend bool operator<(const Base&, const Base&);
d1 < d2 :
#1 #2 . —
#2 , , , . ,
d1 < d2 (d1 <=> d2) < 0 . ,
void 0 — , . , - ,
#1 .
, , C++17, . , - . :
, . .
. , , , , , ( ). , :
« » , , ..
a < b 0 < (b <=> a) , , , .
C++17 . . :
struct A { T t; U u; V v; bool operator==(A const& rhs) const { return t == rhs.t && u == rhs.u && v == rhs.v; } bool operator!=(A const& rhs) const { return !(*this == rhs); } bool operator< (A const& rhs) const {
-
std::tie() , .
, : :
struct A { T t; U u; V v; bool operator==(A const& rhs) const { return t == rhs.t && u == rhs.u && v == rhs.v; } strong_ordering operator<=>(A const& rhs) const {
.
<=> < . , .
c != 0 , , (
), .
. C++20 , :
struct A { T t; U u; V v; bool operator==(A const& rhs) const = default; strong_ordering operator<=>(A const& rhs) const = default; };
, . , :
struct A { T t; U u; V v; bool operator==(A const& rhs) const = default; auto operator<=>(A const& rhs) const = default; };
. , , :
struct A { T t; U u; V v; auto operator<=>(A const& rhs) const = default; };
, , . :
operator== ,
operator<=> .
C++20: . . , , , .
PVS-Studio , <=> . , -. , , (. "
"). ++ .
PVS-Studio <, :
bool operator< (A const& rhs) const { return t < rhs.t && u < rhs.u; }
. , - . .
:
Comparisons in C++20 .