C ++ 20 diperkenalkan perbandingan default, alias "angkasa"operator<=>
, yang memungkinkan Anda untuk permintaan compiler yang dihasilkan <
/ <=
/ ==
/ !=
/ >=
/ dan / atau >
operator dengan (?) Pelaksanaan yang jelas / naif ...
auto operator<=>(const MyClass&) const = default;
... tetapi Anda dapat menyesuaikannya untuk situasi yang lebih rumit (dibahas di bawah). Lihat di sini untuk proposal bahasa, yang berisi pembenaran dan diskusi. Jawaban ini tetap relevan untuk C ++ 17 dan sebelumnya, dan untuk wawasan tentang kapan Anda harus menyesuaikan penerapan operator<=>
....
Tampaknya agak tidak membantu C ++ untuk tidak menstandarkan ini sebelumnya, tetapi seringkali struct / class memiliki beberapa anggota data untuk dikecualikan dari perbandingan (misalnya penghitung, hasil yang di-cache, kapasitas penampung, keberhasilan operasi terakhir / kode kesalahan, kursor), seperti serta keputusan yang harus diambil tentang banyak hal termasuk namun tidak terbatas pada:
- bidang mana yang akan dibandingkan pertama kali, misalnya membandingkan anggota tertentu
int
mungkin menghilangkan 99% objek yang tidak sama dengan sangat cepat, sementara map<string,string>
anggota mungkin sering memiliki entri yang identik dan relatif mahal untuk dibandingkan - jika nilai dimuat pada waktu proses, pemrogram mungkin memiliki wawasan kompiler tidak mungkin
- dalam membandingkan string: sensitivitas huruf, kesetaraan spasi dan pemisah, keluar dari konvensi ...
- presisi saat membandingkan float / double
- apakah nilai floating point NaN harus dianggap sama
- membandingkan pointer atau point-to-data (dan jika yang terakhir, bagaimana mengetahui bagaimana pointer ke array dan berapa banyak objek / byte yang perlu dibandingkan)
- apakah urutan penting saat membandingkan wadah yang tidak diurutkan (misalnya
vector
, list
), dan jika demikian apakah boleh untuk mengurutkannya di tempat sebelum membandingkan vs. menggunakan memori ekstra untuk mengurutkan temporer setiap kali perbandingan dilakukan
- berapa banyak elemen array yang saat ini memiliki nilai valid yang harus dibandingkan (apakah ada ukuran di suatu tempat atau sentinel?)
- anggota yang mana yang
union
akan dibandingkan
- normalisasi: misalnya, jenis tanggal memungkinkan di luar rentang hari-dari-bulan atau bulan-tahun, atau objek rasional / pecahan mungkin memiliki 6/8 sementara yang lain memiliki 3/4, yang karena alasan kinerja mereka mengoreksi malas dengan langkah normalisasi terpisah; Anda mungkin harus memutuskan apakah akan memicu normalisasi sebelum melakukan perbandingan
- apa yang harus dilakukan jika petunjuk lemah tidak valid
- bagaimana menangani anggota dan pangkalan yang tidak menerapkan
operator==
dirinya sendiri (tetapi mungkin memiliki compare()
atau operator<
atau str()
atau getter ...)
- kunci apa yang harus diambil saat membaca / membandingkan data yang mungkin ingin diperbarui oleh utas lain
Jadi, agak menyenangkan untuk memiliki kesalahan sampai Anda secara eksplisit memikirkan tentang apa arti perbandingan untuk struktur spesifik Anda, daripada membiarkannya terkompilasi tetapi tidak memberi Anda hasil yang berarti pada waktu proses .
Semua yang dikatakan, akan lebih baik jika C ++ membiarkan Anda mengatakan bool operator==() const = default;
ketika Anda memutuskan ==
tes anggota-demi-anggota yang "naif" tidak apa - apa. Sama untuk !=
. Beberapa anggota diberikan / basa, "default" <
, <=
, >
, dan >=
implementasi tampak putus asa meskipun - Cascading atas dasar urutan deklarasi ini mungkin tapi sangat tidak mungkin apa yang ingin, mengingat bertentangan imperatif untuk anggota memesan (basa menjadi tentu sebelum anggota, pengelompokan oleh aksesibilitas, konstruksi / penghancuran sebelum penggunaan bergantung). Agar lebih bermanfaat secara luas, C ++ akan membutuhkan sistem anotasi anggota / basis data baru untuk memandu pilihan - itu akan menjadi hal yang hebat untuk dimiliki dalam Standar, idealnya digabungkan dengan pembuatan kode yang ditentukan pengguna berbasis AST ... Saya harap Itu'
Implementasi khas dari operator kesetaraan
Implementasi yang masuk akal
Ini kemungkinan bahwa implementasi yang wajar dan efisien akan menjadi:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.my_struct2 == rhs.my_struct2 &&
lhs.an_int == rhs.an_int;
}
Catatan bahwa ini membutuhkan operator==
untuk MyStruct2
juga.
Implikasi dari implementasi ini, dan alternatifnya, dibahas di bawah judul Diskusi spesifik MyStruct1 Anda di bawah ini.
Pendekatan yang konsisten untuk ==, <,> <= dll
Sangat mudah untuk memanfaatkan std::tuple
operator perbandingan untuk membandingkan instance kelas Anda sendiri - cukup gunakan std::tie
untuk membuat tupel referensi ke bidang dalam urutan perbandingan yang diinginkan. Menggeneralisasikan contoh saya dari sini :
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return std::tie(lhs.my_struct2, lhs.an_int) ==
std::tie(rhs.my_struct2, rhs.an_int);
}
inline bool operator<(const MyStruct1& lhs, const MyStruct1& rhs)
{
return std::tie(lhs.my_struct2, lhs.an_int) <
std::tie(rhs.my_struct2, rhs.an_int);
}
// ...etc...
Saat Anda "memiliki" (yaitu dapat mengedit, faktor dengan lib perusahaan dan pihak ketiga) kelas yang ingin Anda bandingkan, dan terutama dengan kesiapan C ++ 14 untuk menyimpulkan jenis kembalian fungsi dari return
pernyataan, sering kali lebih baik menambahkan " ikat "fungsi anggota ke kelas yang ingin Anda bandingkan:
auto tie() const { return std::tie(my_struct1, an_int); }
Kemudian perbandingan di atas disederhanakan menjadi:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.tie() == rhs.tie();
}
Jika Anda menginginkan kumpulan operator perbandingan yang lebih lengkap, saya sarankan operator tingkatkan (cari less_than_comparable
). Jika tidak cocok karena alasan tertentu, Anda mungkin menyukai atau tidak menyukai gagasan makro dukungan (online) :
#define TIED_OP(STRUCT, OP, GET_FIELDS) \
inline bool operator OP(const STRUCT& lhs, const STRUCT& rhs) \
{ \
return std::tie(GET_FIELDS(lhs)) OP std::tie(GET_FIELDS(rhs)); \
}
#define TIED_COMPARISONS(STRUCT, GET_FIELDS) \
TIED_OP(STRUCT, ==, GET_FIELDS) \
TIED_OP(STRUCT, !=, GET_FIELDS) \
TIED_OP(STRUCT, <, GET_FIELDS) \
TIED_OP(STRUCT, <=, GET_FIELDS) \
TIED_OP(STRUCT, >=, GET_FIELDS) \
TIED_OP(STRUCT, >, GET_FIELDS)
... yang kemudian dapat digunakan a la ...
#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)
(Versi dasi anggota C ++ 14 di sini )
Diskusi spesifik MyStruct1 Anda
Ada implikasi pada pilihan untuk memberikan status bebas versus anggota operator==()
...
Implementasi berdiri bebas
Anda harus membuat keputusan yang menarik. Karena kelas Anda dapat secara implisit dibangun dari a MyStruct2
, fungsi berdiri bebas / non-anggota bool operator==(const MyStruct2& lhs, const MyStruct2& rhs)
akan mendukung ...
my_MyStruct2 == my_MyStruct1
... dengan terlebih dahulu membuat temporary MyStruct1
from my_myStruct2
, lalu melakukan perbandingan. Ini pasti akan meninggalkan MyStruct1::an_int
set ke nilai parameter default konstruktor -1
. Tergantung pada apakah Anda termasuk an_int
perbandingan dalam pelaksanaan Anda operator==
, sebuah MyStruct1
kekuatan atau mungkin tidak membandingkan sama dengan MyStruct2
itu sendiri membandingkan sama dengan MyStruct1
's my_struct_2
anggota! Lebih lanjut, membuat sementara MyStruct1
bisa menjadi operasi yang sangat tidak efisien, karena melibatkan penyalinan my_struct2
anggota yang ada ke sementara, hanya untuk membuangnya setelah perbandingan. (Tentu saja, Anda dapat mencegah konstruksi implisit MyStruct1
s ini untuk perbandingan dengan membuat konstruktor tersebut explicit
atau menghapus nilai default untuk an_int
.)
Implementasi anggota
Jika Anda ingin menghindari konstruksi implisit MyStruct1
dari a MyStruct2
, jadikan operator perbandingan sebagai fungsi anggota:
struct MyStruct1
{
...
bool operator==(const MyStruct1& rhs) const
{
return tie() == rhs.tie(); // or another approach as above
}
};
Perhatikan const
kata kunci - hanya diperlukan untuk implementasi anggota - menyarankan compiler bahwa membandingkan objek tidak mengubahnya, jadi dapat diizinkan pada const
objek.
Membandingkan representasi yang terlihat
Terkadang cara termudah untuk mendapatkan jenis perbandingan yang Anda inginkan adalah ...
return lhs.to_string() == rhs.to_string();
... yang seringkali juga sangat mahal - semua string
itu dibuat dengan susah payah hanya untuk dibuang! Untuk tipe dengan nilai floating point, membandingkan representasi yang terlihat berarti jumlah digit yang ditampilkan menentukan toleransi di mana nilai yang hampir sama diperlakukan sama selama perbandingan.
struct
kesetaraan Anda? Dan jika Anda menginginkan cara yang sederhana, selalu adamemcmp
terlalu lama struct Anda tidak berisi pointer.