Apakah == dan! = Saling tergantung?


292

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 == bbenar, maka a != bsecara otomatis salah, dan sebaliknya, dan tidak ada kemungkinan lain, karena, menurut definisi, a != badalah !(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?


13
Dua petunjuk keduanya bisa nol tetapi tidak harus sama.
Ali Caglayan

2
Tidak yakin apakah itu masuk akal di sini, tetapi membaca ini membuat saya berpikir tentang masalah 'korsleting'. Misalnya, seseorang dapat mendefinisikan bahwa 'undefined' != expressionselalu benar (atau salah, atau tidak terdefinisi), terlepas dari apakah ekspresi dapat dievaluasi. Dalam hal ini a!=bakan mengembalikan hasil yang benar sesuai definisi, tetapi !(a==b)akan gagal jika btidak dapat dievaluasi. (Atau butuh banyak waktu jika evaluasi bitu mahal).
Dennis Jaheruddin

2
Bagaimana dengan null! = Null dan null == null? Bisa jadi keduanya ... jadi jika a! = B itu tidak selalu berarti a == b.
zozo

4
Contoh dari javascript(NaN != NaN) == true
chiliNUT

Jawaban:


272

Anda tidak ingin bahasa tersebut ditulis ulang secara otomatis a != bseperti !(a == b)ketika a == bmengembalikan sesuatu selain a bool. Dan ada beberapa alasan mengapa Anda membuatnya melakukannya.

Anda mungkin memiliki objek pembuat ekspresi, di mana a == btidak dan tidak dimaksudkan untuk melakukan perbandingan apa pun, tetapi cukup membuat beberapa simpul ekspresi yang mewakili a == b.

Anda mungkin memiliki evaluasi yang malas, di mana a == btidak dan tidak dimaksudkan untuk melakukan perbandingan secara langsung, tetapi sebaliknya mengembalikan beberapa jenis lazy<bool>yang dapat dikonversi menjadi boolsecara 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 dan u, Anda ingin mengizinkan t == u, tetapi membuatnya kembali optional<bool>.

Mungkin ada lebih banyak yang tidak saya pikirkan. Dan meskipun dalam contoh-contoh ini operasi a == bdan a != bkeduanya masuk akal, masih a != bbukan hal yang sama !(a == b), jadi definisi yang terpisah diperlukan.


72
Ekspresi bangunan adalah contoh praktis fantastis kapan Anda menginginkannya, yang tidak bergantung pada skenario yang dibuat-buat.
Oliver Charlesworth

6
Contoh bagus lainnya adalah operasi vektor logis. Anda lebih suka satu melewati komputasi data !=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.

41
"Anda mungkin memiliki ekspresi pembangun benda" - baik maka operator !juga dapat membangun beberapa ekspresi node dan kami masih baik-baik saja mengganti a != bdengan !(a == b), sejauh yang masuk. Sama berlaku untuk lazy<bool>::operator!, itu bisa kembali lazy<bool>. optional<bool>lebih meyakinkan, karena kebenaran logis misalnya boost::optionaltergantung pada apakah suatu nilai ada, bukan pada nilai itu sendiri.
Steve Jessop

42
Semua itu, dan Nans - harap ingat NaNs;
jsbueno

9
@ jsbueno: telah ditunjukkan lebih jauh ke bawah bahwa NaNs tidak istimewa dalam hal ini.
Oliver Charlesworth

110

Jika tidak ada kemungkinan seperti itu, lalu mengapa di Bumi apakah C ++ memiliki dua operator yang didefinisikan sebagai dua fungsi yang berbeda?

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 di std::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.


18
Ini adalah satu-satunya jawaban yang masuk akal secara praktis.
Sonic Atom

2
Bagi saya sepertinya Anda memiliki sebab dan akibat ke belakang. Anda dapat membebani mereka secara terpisah karena ==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).
nitro2k01

60

Kekhawatiran saya adalah, mengapa ada dua definisi terpisah yang dibutuhkan?

Anda tidak harus mendefinisikan keduanya.
Jika mereka saling eksklusif, Anda masih bisa ringkas dengan hanya mendefinisikan ==dan <bersama std :: rel_ops

Preferensi cp:

#include <iostream>
#include <utility>

struct Foo {
    int n;
};

bool operator==(const Foo& lhs, const Foo& rhs)
{
    return lhs.n == rhs.n;
}

bool operator<(const Foo& lhs, const Foo& rhs)
{
    return lhs.n < rhs.n;
}

int main()
{
    Foo f1 = {1};
    Foo f2 = {2};
    using namespace std::rel_ops;

    //all work as you would expect
    std::cout << "not equal:     : " << (f1 != f2) << '\n';
    std::cout << "greater:       : " << (f1 > f2) << '\n';
    std::cout << "less equal:    : " << (f1 <= f2) << '\n';
    std::cout << "greater equal: : " << (f1 >= f2) << '\n';
}

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?

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.

(baik dari perspektif pengguna, atau perspektif pelaksana)

Saya tahu Anda menginginkan contoh spesifik,
jadi di sini ada satu dari kerangka kerja pengujian Catch yang menurut saya praktis:

template<typename RhsT>
ResultBuilder& operator == ( RhsT const& rhs ) {
    return captureExpression<Internal::IsEqualTo>( rhs );
}

template<typename RhsT>
ResultBuilder& operator != ( RhsT const& rhs ) {
    return captureExpression<Internal::IsNotEqualTo>( rhs );
}

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.


14
Ya ampun, bagaimana mungkin aku tidak tahu std::rel_ops? Terima kasih banyak untuk menunjukkannya.
Daniel Jour

5
Salinan hampir-kata demi kata dari cppreference (atau tempat lain) harus ditandai dengan jelas dan dikaitkan dengan benar. rel_opsitu mengerikan.
TC

@TC Setuju, saya hanya mengatakan metode yang bisa diambil OP. Saya tidak tahu bagaimana menjelaskan rel_ops lebih sederhana dari contoh yang ditunjukkan. Saya ditautkan ke tempat itu, tetapi kode yang diposting karena halaman referensi selalu dapat berubah.
Trevor Hickey

4
Anda masih perlu memperjelas bahwa contoh kode adalah 99% dari cppreference, bukan milik Anda.
TC

2
Std :: relops tampaknya tidak disukai. Lihat peningkatan boost untuk sesuatu yang lebih bertarget.
JDługosz

43

Ada beberapa konvensi sangat mapan di mana (a == b)dan (a != b)yang kedua palsu tidak 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.


4
Mengimplementasikan perilaku null seperti SQL di C ++? Ewwww. Tapi saya kira itu bukan sesuatu yang saya pikir harus dilarang dalam bahasa ini, betapapun tidak menyenangkannya itu.

1
@ dan1111 Lebih penting lagi, beberapa rasa SQL mungkin dikodekan dalam c ++, jadi bahasa perlu mendukung sintaks mereka, bukan?
Joe

1
Perbaiki saya jika saya salah, saya hanya pergi dari wikipedia di sini, tetapi tidak membandingkannya dengan nilai NULL dalam pengembalian SQL Tidak Diketahui, bukan Salah? Dan bukankah negasi dari Unknown masih Unknown? Jadi jika logika SQL dikodekan dalam C ++, tidakkah Anda ingin NULL == somethingmengembalikan Unknown, dan Anda juga ingin NULL != somethingmengembalikan Unknown, dan Anda ingin !Unknownkembali Unknown. Dan dalam hal itu implementasi operator!=sebagai negasi operator==masih benar.
Benjamin Lindley

1
@Barmar: Oke, tapi lalu bagaimana itu membuat pernyataan "SQL NULLs bekerja dengan cara ini" benar? Jika kita membatasi implementasi operator perbandingan kita untuk mengembalikan boolean, bukankah itu berarti mengimplementasikan logika SQL dengan operator ini tidak mungkin?
Benjamin Lindley

2
@Barmar: Ya tidak, bukan itu intinya. OP sudah tahu fakta itu, atau pertanyaan ini tidak akan ada. Intinya adalah untuk menyajikan contoh di mana masuk akal 1) untuk mengimplementasikan salah satu operator==atau operator!=, tetapi tidak yang lain, atau 2) untuk mengimplementasikan operator!=dengan cara selain negasi operator==. Dan menerapkan logika SQL untuk nilai-nilai NULL bukan kasus itu.
Benjamin Lindley

23

Saya hanya akan menjawab bagian kedua dari pertanyaan Anda, yaitu:

Jika tidak ada kemungkinan seperti itu, lalu mengapa di Bumi apakah C ++ memiliki dua operator yang didefinisikan sebagai dua fungsi yang berbeda?

Salah satu alasan mengapa masuk akal untuk membiarkan pengembang membebani keduanya adalah kinerja. Anda dapat mengizinkan pengoptimalan dengan menerapkan keduanya ==dan !=. Maka x != ymungkin 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- ):

$ ghci
GHCi, version 7.10.2: http://www.haskell.org/ghc/  :? for help
λ> :i Eq
class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool
        -- Defined in `GHC.Classes'

Ini mungkin akan dianggap optimasi mikro, tetapi mungkin diperlukan untuk beberapa kasus.


3
Kelas wrapper SSE (x86 SIMD) adalah contoh yang bagus untuk ini. Ada sebuah pcmpeqbinstruksi, 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-perbandingan neq. Sayang sekali Intel tidak mengadopsi / memperpanjang XOP; ada beberapa instruksi yang berguna dalam ekstensi ISA yang akan segera mati.)
Peter Cordes

1
Seluruh titik SIMD di tempat pertama adalah kinerja, dan Anda biasanya hanya repot menggunakannya secara manual dalam loop yang penting untuk kinerja keseluruhan. Menyimpan instruksi tunggal ( PXORdengan semua yang bisa membalikkan hasil topeng pembanding) dalam loop ketat bisa menjadi masalah.
Peter Cordes

Kinerja sebagai alasan tidak dapat dipercaya ketika overhead adalah satu negasi logis .
Ceria dan hth. - Alf

Mungkin lebih dari satu negasi logis jika x == ybiaya komputasi lebih signifikan daripada x != y. Menghitung yang terakhir mungkin jauh lebih murah karena prediksi cabang, dll.
Centril

16

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)

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).


13

Menanggapi hasil edit;

Yaitu, jika mungkin untuk beberapa jenis memiliki operator ==tetapi tidak !=, atau sebaliknya, dan kapan masuk akal untuk melakukannya.

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_opsnamespace. 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_opsmenjadi 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.


12

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.


7

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).


6
enum BoolPlus {
    kFalse = 0,
    kTrue = 1,
    kFileNotFound = -1
}

BoolPlus operator==(File& other);
BoolPlus operator!=(File& other);

Saya tidak dapat membenarkan kelebihan operator ini, tetapi dalam contoh di atas tidak mungkin untuk didefinisikan operator!=sebagai "lawan" dari operator==.



1
@Snowman: Dafang tidak mengatakan itu enumerasi yang baik (atau ide yang baik untuk mendefinisikan enumerasi seperti itu), itu hanya sebuah contoh untuk menggambarkan suatu poin. Dengan definisi operator ini (mungkin buruk), maka !=memang tidak akan berarti sebaliknya ==.
AlainD

1
@ AlainD apakah Anda mengklik tautan yang saya posting, dan apakah Anda mengetahui tujuan dari situs itu? Ini disebut "humor."

1
@Snowman: Saya tentu saja ... maaf, saya melewatkan itu adalah tautan dan dimaksudkan sebagai ironi! : o)
AlainD

Tunggu, Anda kelebihan beban unary ==?
LF

5

Pada akhirnya, apa yang Anda periksa dengan operator itu adalah ekspresi a == b atau a != bmengembalikan nilai Boolean ( trueatau false). Ungkapan ini mengembalikan nilai Boolean setelah perbandingan daripada menjadi saling eksklusif.


4

[..] mengapa ada dua definisi terpisah yang dibutuhkan?

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.)

[..] menurut definisi, a != badalah !(a == b).

Dan itu adalah tanggung jawab Anda sebagai programmer untuk bertahan. Mungkin hal yang baik untuk menulis ujian.


4
Bagaimana !((a == rhs.a) && (b == rhs.b))tidak memungkinkan hubungan arus pendek? jika !(a == rhs.a), maka (b == rhs.b)tidak akan dievaluasi.
Benjamin Lindley

Ini adalah contoh yang buruk. Hubungan arus pendek tidak menambah keuntungan magis di sini.
Oliver Charlesworth

@ Oliver Charlesworth Sendiri tidak, tetapi ketika bergabung dengan operator yang berbeda, itu tidak: Dalam kasus ==, 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.
BarbaraKwarc

@ BenjaminLindley Benar, contoh saya benar-benar omong kosong. Sayangnya, saya tidak dapat menemukan atm yang lain, sudah terlambat di sini.
Daniel Jour

1
@BarbaraKwarc: !((a == b) && (c == d))dan (a != b) || (c != d)setara dalam hal efisiensi hubungan arus pendek.
Oliver Charlesworth

2

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.


2

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.


Mereka tidak saling eksklusif untuk semua kasus. Sebagai contoh, dua infinitas keduanya tidak sama satu sama lain dan tidak sama satu sama lain.
vladon

@vladon dapat menggunakan salah satu daripada yang lain dalam kasus generik ? Ini berarti mereka tidak sama. Semua sisanya pergi ke fungsi khusus daripada ke operator == /! =
oliora

@vladon tolong, alih-alih kasus umum baca semua kasus dalam jawaban saya.
oliora

@vladon Sebanyak ini berlaku dalam matematika, dapatkah Anda memberikan contoh di mana a != btidak sama dengan !(a == b)untuk alasan ini dalam C?
nitro2k01

2

Mungkin aturan dicarikan tandingannya, di mana a != badalah palsu dan a == bitu palsu seperti sedikit stateless.

if( !(a == b || a != b) ){
    // Stateless
}

Jika Anda ingin mengatur ulang simbol logis maka! ([A] || [B]) secara logis menjadi ([! A] & [! B])
Thijser

Perhatikan bahwa tipe kembalinya operator==()dan operator!=()belum tentu bool, mereka mungkin enum yang menyertakan stateless jika Anda menginginkannya, namun operator mungkin masih didefinisikan, jadi (a != b) == !(a==b)
tahan
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.