Saya belajar tentang kelebihan operator di C ++, dan saya melihat itu ==
dan !=
hanya beberapa fungsi khusus yang dapat dikustomisasi untuk tipe yang ditentukan pengguna. Kekhawatiran saya adalah, mengapa ada dua definisi terpisah yang dibutuhkan? Saya berpikir bahwa jika a == b
benar, maka a != b
secara otomatis salah, dan sebaliknya, dan tidak ada kemungkinan lain, karena, menurut definisi, a != b
adalah !(a == b)
. Dan saya tidak bisa membayangkan situasi di mana ini tidak benar. Tapi mungkin imajinasiku terbatas atau aku tidak tahu sesuatu?
Saya tahu bahwa saya dapat mendefinisikan satu dalam hal yang lain, tetapi ini bukan yang saya tanyakan. Saya juga tidak bertanya tentang perbedaan antara membandingkan objek berdasarkan nilai atau identitas. Atau apakah dua objek bisa sama dan tidak sama pada saat yang sama (ini jelas bukan pilihan! Hal-hal ini saling eksklusif). Yang saya tanyakan adalah ini:
Apakah ada situasi yang memungkinkan di mana mengajukan pertanyaan tentang dua objek yang sama itu masuk akal, tetapi bertanya tentang mereka yang tidak sama tidak masuk akal? (baik dari perspektif pengguna, atau perspektif pelaksana)
Jika tidak ada kemungkinan seperti itu, lalu mengapa di Bumi apakah C ++ memiliki dua operator yang didefinisikan sebagai dua fungsi yang berbeda?
sumber
'undefined' != expression
selalu benar (atau salah, atau tidak terdefinisi), terlepas dari apakah ekspresi dapat dievaluasi. Dalam hal inia!=b
akan mengembalikan hasil yang benar sesuai definisi, tetapi!(a==b)
akan gagal jikab
tidak dapat dievaluasi. (Atau butuh banyak waktu jika evaluasib
itu mahal).(NaN != NaN) == true
Jawaban:
Anda tidak ingin bahasa tersebut ditulis ulang secara otomatis
a != b
seperti!(a == b)
ketikaa == b
mengembalikan sesuatu selain abool
. Dan ada beberapa alasan mengapa Anda membuatnya melakukannya.Anda mungkin memiliki objek pembuat ekspresi, di mana
a == b
tidak dan tidak dimaksudkan untuk melakukan perbandingan apa pun, tetapi cukup membuat beberapa simpul ekspresi yang mewakilia == b
.Anda mungkin memiliki evaluasi yang malas, di mana
a == b
tidak dan tidak dimaksudkan untuk melakukan perbandingan secara langsung, tetapi sebaliknya mengembalikan beberapa jenislazy<bool>
yang dapat dikonversi menjadibool
secara implisit atau eksplisit di beberapa waktu kemudian untuk benar-benar melakukan perbandingan. Mungkin dikombinasikan dengan objek pembuat ekspresi untuk memungkinkan pengoptimalan ekspresi lengkap sebelum evaluasi.Anda mungkin memiliki beberapa
optional<T>
kelas templat khusus , di mana diberikan variabel opsionalt
danu
, Anda ingin mengizinkant == u
, tetapi membuatnya kembalioptional<bool>
.Mungkin ada lebih banyak yang tidak saya pikirkan. Dan meskipun dalam contoh-contoh ini operasi
a == b
dana != b
keduanya masuk akal, masiha != b
bukan hal yang sama!(a == b)
, jadi definisi yang terpisah diperlukan.sumber
!=
daripada dua melewati komputasi==
itu!
. Terutama kembali pada hari di mana Anda tidak bisa bergantung pada kompiler untuk memadukan loop. Atau bahkan hari ini jika Anda gagal meyakinkan kompiler vektor Anda tidak tumpang tindih.!
juga dapat membangun beberapa ekspresi node dan kami masih baik-baik saja menggantia != b
dengan!(a == b)
, sejauh yang masuk. Sama berlaku untuklazy<bool>::operator!
, itu bisa kembalilazy<bool>
.optional<bool>
lebih meyakinkan, karena kebenaran logis misalnyaboost::optional
tergantung pada apakah suatu nilai ada, bukan pada nilai itu sendiri.Nan
s - harap ingatNaN
s;Karena Anda dapat membebani mereka secara berlebihan, dan dengan membebani mereka, Anda dapat memberi mereka arti yang sama sekali berbeda dari yang asli.
Ambil, misalnya, operator
<<
, yang semula operator shift kiri bitwise, sekarang umumnya kelebihan beban sebagai operator penyisipan, seperti distd::cout << something
; makna yang sama sekali berbeda dari yang asli.Jadi, jika Anda menerima bahwa arti dari operator berubah ketika Anda membebani berlebih, maka tidak ada alasan untuk mencegah pengguna memberikan makna kepada operator
==
yang bukan merupakan negasi dari operator!=
, meskipun ini mungkin membingungkan.sumber
==
dan!=
ada sebagai operator yang berbeda. Di sisi lain, mereka mungkin tidak ada sebagai operator yang berbeda karena Anda dapat membebani mereka secara terpisah, tetapi karena alasan warisan dan kenyamanan (singkatnya kode).Anda tidak harus mendefinisikan keduanya.
Jika mereka saling eksklusif, Anda masih bisa ringkas dengan hanya mendefinisikan
==
dan<
bersama std :: rel_opsPreferensi cp:
Kami sering mengaitkan operator ini dengan kesetaraan.
Meskipun itu adalah bagaimana mereka berperilaku pada tipe fundamental, tidak ada kewajiban bahwa ini adalah perilaku mereka pada tipe data kustom. Anda bahkan tidak perlu mengembalikan bool jika Anda tidak mau.
Saya telah melihat orang-orang membebani operator dengan cara yang aneh, hanya untuk menemukan bahwa itu masuk akal untuk aplikasi spesifik domain mereka. Bahkan jika antarmuka muncul untuk menunjukkan bahwa mereka saling eksklusif, penulis mungkin ingin menambahkan logika internal tertentu.
Saya tahu Anda menginginkan contoh spesifik,
jadi di sini ada satu dari kerangka kerja pengujian Catch yang menurut saya praktis:
Operator-operator ini melakukan hal yang berbeda, dan tidak masuk akal untuk mendefinisikan satu metode sebagai! (Bukan) yang lain. Alasan ini dilakukan, adalah agar framework dapat mencetak perbandingan yang dibuat. Untuk melakukan itu, perlu menangkap konteks apa operator kelebihan beban digunakan.
sumber
std::rel_ops
? Terima kasih banyak untuk menunjukkannya.rel_ops
itu mengerikan.Ada beberapa konvensi sangat mapan di mana
(a == b)
dan(a != b)
yangkedua palsutidak selalu berlawanan. Secara khusus, dalam SQL, perbandingan apa pun dengan NULL menghasilkan NULL, tidak benar atau salah.Mungkin bukan ide yang baik untuk membuat contoh baru ini jika memungkinkan, karena sangat tidak intuitif, tetapi jika Anda mencoba untuk memodelkan konvensi yang ada, senang memiliki opsi untuk membuat operator Anda berperilaku "dengan benar" untuk itu konteks.
sumber
NULL == something
mengembalikan Unknown, dan Anda juga inginNULL != something
mengembalikan Unknown, dan Anda ingin!Unknown
kembaliUnknown
. Dan dalam hal itu implementasioperator!=
sebagai negasioperator==
masih benar.operator==
atauoperator!=
, tetapi tidak yang lain, atau 2) untuk mengimplementasikanoperator!=
dengan cara selain negasioperator==
. Dan menerapkan logika SQL untuk nilai-nilai NULL bukan kasus itu.Saya hanya akan menjawab bagian kedua dari pertanyaan Anda, yaitu:
Salah satu alasan mengapa masuk akal untuk membiarkan pengembang membebani keduanya adalah kinerja. Anda dapat mengizinkan pengoptimalan dengan menerapkan keduanya
==
dan!=
. Makax != y
mungkin lebih murah daripada!(x == y)
ini. Beberapa kompiler mungkin dapat mengoptimalkannya untuk Anda, tetapi mungkin tidak, terutama jika Anda memiliki objek kompleks dengan banyak percabangan yang terlibat.Bahkan di Haskell, di mana pengembang mengambil hukum dan konsep matematika dengan sangat serius, satu masih diperbolehkan untuk membebani keduanya
==
dan/=
, seperti yang Anda lihat di sini ( http://hackage.haskell.org/package/base-4.9.0.0/docs/Prelude .html # v: -61--61- ):Ini mungkin akan dianggap optimasi mikro, tetapi mungkin diperlukan untuk beberapa kasus.
sumber
pcmpeqb
instruksi, tetapi tidak ada instruksi yang dikemas-bandingkan yang menghasilkan topeng! =. Jadi, jika Anda tidak bisa membalikkan logika apa pun yang menggunakan hasilnya, Anda harus menggunakan instruksi lain untuk membalikkannya. (Fakta yang menyenangkan: set instruksi XOP AMD memang memiliki paket-perbandinganneq
. Sayang sekali Intel tidak mengadopsi / memperpanjang XOP; ada beberapa instruksi yang berguna dalam ekstensi ISA yang akan segera mati.)PXOR
dengan semua yang bisa membalikkan hasil topeng pembanding) dalam loop ketat bisa menjadi masalah.x == y
biaya komputasi lebih signifikan daripadax != y
. Menghitung yang terakhir mungkin jauh lebih murah karena prediksi cabang, dll.Itu pendapat. Mungkin tidak. Tetapi para perancang bahasa, yang tidak mahatahu, memutuskan untuk tidak membatasi orang-orang yang mungkin menemukan situasi di mana hal itu mungkin masuk akal (setidaknya bagi mereka).
sumber
Menanggapi hasil edit;
Secara umum , tidak, itu tidak masuk akal. Kesetaraan dan operator relasional umumnya datang dalam set. Jika ada kesetaraan, maka ketidaksetaraan juga; kurang dari, kemudian lebih besar dari dan seterusnya dengan
<=
dll. Pendekatan serupa diterapkan pada operator aritmatika juga, mereka juga umumnya datang dalam set logis alami.Ini dibuktikan dalam
std::rel_ops
namespace. Jika Anda menerapkan kesetaraan dan kurang dari operator, menggunakan namespace memberi Anda yang lain, diimplementasikan dalam hal operator yang diterapkan asli Anda.Itu semua mengatakan, apakah ada kondisi atau situasi di mana yang satu tidak akan langsung berarti yang lain, atau tidak dapat diimplementasikan dalam hal yang lain? Ya ada , bisa dibilang sedikit, tetapi mereka ada di sana; lagi, sebagaimana dibuktikan dalam
rel_ops
menjadi namespace sendiri. Karena alasan itu, memungkinkan penerapannya secara independen memungkinkan Anda memanfaatkan bahasa untuk mendapatkan semantik yang Anda butuhkan atau butuhkan dengan cara yang masih alami dan intuitif untuk pengguna atau klien kode.Evaluasi malas yang telah disebutkan adalah contoh yang bagus untuk ini. Contoh bagus lainnya adalah memberi mereka semantik yang tidak berarti kesetaraan atau ketidaksetaraan sama sekali. Contoh serupa dengan ini adalah operator bit shift
<<
dan>>
digunakan untuk penyisipan dan ekstraksi aliran. Meskipun mungkin disukai di kalangan umum, di beberapa daerah domain tertentu mungkin masuk akal.sumber
Jika operator
==
dan!=
tidak benar-benar menyiratkan kesetaraan, dengan cara yang sama bahwa operator<<
dan>>
streaming tidak menyiratkan pergeseran bit. Jika Anda memperlakukan simbol seolah-olah itu berarti beberapa konsep lain, mereka tidak harus saling eksklusif.Dalam hal kesetaraan, masuk akal jika use-case Anda menjamin objek yang tidak dapat dibandingkan, sehingga setiap perbandingan akan menghasilkan false (atau tipe hasil yang tidak dapat dibandingkan, jika operator Anda mengembalikan non-bool). Saya tidak bisa memikirkan situasi tertentu di mana ini akan dibenarkan, tetapi saya bisa melihatnya cukup masuk akal.
sumber
Dengan kekuatan besar hadir tanggung jawab besar, atau setidaknya panduan gaya yang benar-benar bagus.
==
dan!=
dapat kelebihan beban untuk melakukan apa pun yang Anda inginkan. Itu adalah berkah sekaligus kutukan. Tidak ada jaminan!=
artinya!(a==b)
.sumber
Saya tidak dapat membenarkan kelebihan operator ini, tetapi dalam contoh di atas tidak mungkin untuk didefinisikan
operator!=
sebagai "lawan" darioperator==
.sumber
!=
memang tidak akan berarti sebaliknya==
.==
?Pada akhirnya, apa yang Anda periksa dengan operator itu adalah ekspresi
a == b
ataua != b
mengembalikan nilai Boolean (true
ataufalse
). Ungkapan ini mengembalikan nilai Boolean setelah perbandingan daripada menjadi saling eksklusif.sumber
Satu hal yang perlu dipertimbangkan adalah kemungkinan ada kemungkinan penerapan salah satu operator ini lebih efisien daripada hanya menggunakan negasi yang lain.
(Contoh saya di sini adalah sampah, tetapi intinya masih berlaku, pikirkan filter bloom, misalnya: Mereka memungkinkan pengujian cepat jika ada sesuatu yang tidak dalam satu set, tetapi pengujian jika dalam mungkin memerlukan lebih banyak waktu.)
Dan itu adalah tanggung jawab Anda sebagai programmer untuk bertahan. Mungkin hal yang baik untuk menulis ujian.
sumber
!((a == rhs.a) && (b == rhs.b))
tidak memungkinkan hubungan arus pendek? jika!(a == rhs.a)
, maka(b == rhs.b)
tidak akan dievaluasi.==
, ia akan berhenti membandingkan segera setelah elemen yang sesuai pertama tidak sama. Tetapi dalam kasus!=
, jika diterapkan dalam hal==
, perlu membandingkan semua elemen yang sesuai terlebih dahulu (ketika mereka semua sama) untuk dapat mengatakan bahwa mereka tidak tidak sama: P Tetapi ketika diimplementasikan seperti pada contoh di atas, ia akan berhenti membandingkan begitu menemukan pasangan tidak sama pertama. Contoh yang bagus.!((a == b) && (c == d))
dan(a != b) || (c != d)
setara dalam hal efisiensi hubungan arus pendek.Dengan menyesuaikan perilaku operator, Anda dapat membuat mereka melakukan apa yang Anda inginkan.
Anda mungkin ingin menyesuaikan hal-hal. Misalnya, Anda mungkin ingin mengkustomisasi kelas. Objek kelas ini dapat dibandingkan hanya dengan memeriksa properti tertentu. Mengetahui hal ini, Anda dapat menulis beberapa kode spesifik yang hanya memeriksa hal-hal minimum, alih-alih memeriksa setiap bit dari setiap properti di seluruh objek.
Bayangkan sebuah kasus di mana Anda dapat mengetahui bahwa ada sesuatu yang berbeda secepat, jika tidak lebih cepat, daripada Anda dapat menemukan sesuatu yang sama. Memang, begitu Anda mengetahui apakah sesuatu itu sama atau berbeda, maka Anda dapat mengetahui sebaliknya hanya dengan membalik sedikit. Namun, membalik bit itu adalah operasi ekstra. Dalam beberapa kasus, ketika kode dieksekusi ulang banyak, menyimpan satu operasi (dikalikan berkali-kali) dapat memiliki peningkatan kecepatan secara keseluruhan. (Misalnya, jika Anda menyimpan satu operasi per piksel layar megapiksel, maka Anda baru saja menyimpan satu juta operasi. Dikalikan 60 layar per detik, dan Anda bahkan menghemat lebih banyak operasi.)
Jawaban hvd memberikan beberapa contoh tambahan.
sumber
Ya, karena satu berarti "setara" dan lainnya berarti "tidak setara" dan istilah ini saling eksklusif. Makna lain untuk operator ini membingungkan dan harus dihindari dengan segala cara.
sumber
a != b
tidak sama dengan!(a == b)
untuk alasan ini dalam C?Mungkin aturan dicarikan tandingannya, di mana
a != b
adalah palsu dana == b
itu palsu seperti sedikit stateless.sumber
operator==()
danoperator!=()
belum tentubool
, mereka mungkin enum yang menyertakan stateless jika Anda menginginkannya, namun operator mungkin masih didefinisikan, jadi(a != b) == !(a==b)