== และ! = ขึ้นอยู่กับซึ่งกันและกันหรือไม่


292

ฉันเรียนรู้เกี่ยวกับการดำเนินงานมากใน C ++ และผมเห็นว่า==และ!=เป็นเพียงบางฟังก์ชั่นพิเศษที่สามารถปรับแต่งสำหรับประเภทที่ผู้ใช้กำหนด แม้ว่าข้อกังวลของฉันคือทำไมต้องมีคำจำกัดความแยกจากกันสองประการ ผมคิดว่าถ้าa == bเป็นจริงแล้วa != bเป็นเท็จโดยอัตโนมัติและในทางกลับกันและไม่มีความเป็นไปได้อื่น ๆ เพราะโดยความหมายคือa != b !(a == b)และฉันไม่สามารถจินตนาการถึงสถานการณ์ที่สิ่งนี้ไม่เป็นความจริง แต่บางทีจินตนาการของฉันมี จำกัด หรือฉันไม่รู้อะไรเลย?

ฉันรู้ว่าฉันสามารถกำหนดหนึ่งในแง่ของอื่น ๆ แต่นี่ไม่ใช่สิ่งที่ฉันถามเกี่ยวกับ ฉันยังไม่ได้ถามถึงความแตกต่างระหว่างการเปรียบเทียบวัตถุด้วยมูลค่าหรือเอกลักษณ์ หรือว่าวัตถุสองรายการอาจเท่ากันและไม่เท่ากันในเวลาเดียวกัน (นี่ไม่ใช่ตัวเลือกแน่นอน! สิ่งเหล่านี้เป็นสิ่งที่ไม่เกิดร่วมกัน) สิ่งที่ฉันถามคือ:

มีสถานการณ์ใดบ้างที่เป็นไปได้ที่การถามคำถามเกี่ยวกับวัตถุสองชิ้นที่เท่ากันนั้นสมเหตุสมผล แต่การถามเกี่ยวกับวัตถุที่ไม่เท่ากันนั้นไม่สมเหตุสมผล (จากมุมมองของผู้ใช้หรือมุมมองของผู้ใช้)

หากไม่มีความเป็นไปได้เช่นนั้นเหตุใด C ++ ในโลกจึงให้ผู้ให้บริการสองรายนี้ถูกกำหนดให้เป็นฟังก์ชั่นที่แตกต่างกันสองรายการ


13
ตัวชี้สองตัวอาจเป็นโมฆะ แต่ไม่จำเป็นต้องเท่ากัน
Ali Caglayan

2
ไม่แน่ใจว่ามันสมเหตุสมผลหรือไม่ แต่การอ่านสิ่งนี้ทำให้ฉันคิดถึงปัญหา 'ไฟฟ้าลัดวงจร' ตัวอย่างเช่นหนึ่งสามารถกำหนดที่'undefined' != expressionเป็นจริงเสมอ (หรือเท็จหรือไม่ได้กำหนด) โดยไม่คำนึงถึงว่าสามารถประเมินการแสดงออก ในกรณีนี้a!=bจะส่งคืนผลลัพธ์ที่ถูกต้องตามคำนิยาม แต่!(a==b)จะล้มเหลวหากbไม่สามารถประเมินได้ (หรือใช้เวลามากในการประเมินbราคาแพง)
Dennis Jaheruddin

2
แล้ว null ล่ะ! = null และ null == null? อาจเป็นได้ทั้ง ... ดังนั้นถ้า a = b ที่ไม่ได้หมายถึง a == b เสมอไป
zozo

4
ตัวอย่างจาก javascript(NaN != NaN) == true
chiliNUT

คำตอบ:


272

คุณจะไม่ต้องการภาษาโดยอัตโนมัติเขียนa != bเป็น!(a == b)เมื่อผลตอบแทนบางสิ่งบางอย่างอื่นที่ไม่ใช่a == b boolและมีสาเหตุบางประการที่ทำให้คุณทำเช่นนั้น

คุณอาจจะมีวัตถุสร้างการแสดงออกที่a == bไม่ได้และไม่ได้มีวัตถุประสงค์เพื่อดำเนินการเปรียบเทียบใด ๆ a == bแต่เพียงสร้างบางโหนดการแสดงออกที่เป็นตัวแทนของ

คุณอาจมีการประเมินผลที่ขี้เกียจซึ่งa == bไม่ได้และไม่ได้มีวัตถุประสงค์เพื่อทำการเปรียบเทียบใด ๆ โดยตรง แต่แทนที่จะส่งกลับบางประเภทlazy<bool>ที่สามารถแปลงเป็นboolโดยปริยายหรืออย่างชัดเจนในภายหลังเพื่อทำการเปรียบเทียบจริง ๆ อาจรวมกับออบเจ็กต์ตัวสร้างนิพจน์เพื่ออนุญาตการปรับให้เหมาะสมของนิพจน์ก่อนการประเมิน

คุณอาจมีoptional<T>คลาสเทมเพลตที่กำหนดเองซึ่งมีตัวแปรที่เป็นตัวเลือกtและuคุณต้องการอนุญาตt == uแต่ให้คืนoptional<bool>ค่า

อาจมีมากกว่าที่ฉันไม่ได้คิด และถึงแม้ว่าในตัวอย่างเหล่านี้การดำเนินการa == bและa != bทำทั้งสองอย่างสมเหตุสมผล แต่ก็ยังa != bไม่เหมือนกัน!(a == b)ดังนั้นจึงจำเป็นต้องมีคำจำกัดความแยกต่างหาก


72
การสร้างนิพจน์เป็นตัวอย่างที่ใช้งานได้จริงที่ยอดเยี่ยมเมื่อคุณต้องการสิ่งนี้ซึ่งไม่ต้องอาศัยสถานการณ์จำลอง
Oliver Charlesworth

6
อีกตัวอย่างที่ดีคือการดำเนินการเชิงตรรกะของเวกเตอร์ คุณอยากจะเป็นหนึ่งผ่านข้อมูลคอมพิวเตอร์!=แทนของทั้งสองผ่านการคำนวณแล้ว== !โดยเฉพาะอย่างยิ่งย้อนกลับไปในวันที่คุณไม่สามารถพึ่งพาคอมไพเลอร์เพื่อหลอมรวมลูป หรือแม้กระทั่งทุกวันนี้ถ้าคุณไม่ยอมโน้มน้าวให้คอมไพเลอร์เวกเตอร์ของคุณไม่ทับซ้อนกัน

41
"คุณอาจมีออบเจ็กต์ตัวสร้างนิพจน์" - จากนั้นโอเปอเรเตอร์ก็!สามารถสร้างโหนดนิพจน์บางตัวได้และเราก็ยังสามารถแทนที่a != bด้วย!(a == b)ได้ เดียวกันจะไปสำหรับมันสามารถกลับlazy<bool>::operator! มีความน่าเชื่อถือมากกว่าเนื่องจากความจริงเชิงตรรกะของตัวอย่างขึ้นอยู่กับว่าคุณค่านั้นมีอยู่หรือไม่ lazy<bool>optional<bool>boost::optional
Steve Jessop

42
ทั้งหมดนั้นและNans - โปรดจดจำ NaNs;
jsbueno

9
@jsbueno: มีการชี้ให้เห็นเพิ่มเติมว่า NaNs ไม่ได้มีความพิเศษในเรื่องนี้
Oliver Charlesworth

110

หากไม่มีความเป็นไปได้เช่นนั้นเหตุใด C ++ ในโลกจึงให้ผู้ให้บริการสองรายนี้ถูกกำหนดให้เป็นฟังก์ชั่นที่แตกต่างกันสองรายการ

เนื่องจากคุณสามารถบรรทุกเกินพิกัดและโดยการบรรทุกเกินพิกัดคุณสามารถให้ความหมายที่แตกต่างไปจากเดิมอย่างสิ้นเชิง

ใช้ตัวอย่างเช่นผู้ประกอบการ<<เดิมค่าบิตซ้ายประกอบกะตอนนี้มากเกินไปทั่วไปว่าเป็นผู้ประกอบการแทรกเหมือนในstd::cout << something; ความหมายที่แตกต่างไปจากเดิมอย่างสิ้นเชิง

ดังนั้นหากคุณยอมรับว่าความหมายของโอเปอเรเตอร์เปลี่ยนไปเมื่อคุณโอเวอร์โหลดมากเกินไปก็ไม่มีเหตุผลที่จะป้องกันผู้ใช้จากการให้ความหมายกับโอเปอเรเตอร์==ซึ่งไม่ใช่การปฏิเสธของผู้ให้บริการ!=แม้ว่าจะทำให้เกิดความสับสน


18
นี่เป็นคำตอบเดียวที่สมเหตุสมผล
Sonic Atom

2
สำหรับฉันดูเหมือนว่าคุณมีสาเหตุและผลย้อนหลัง คุณสามารถโหลดเกินพิกัดแยกต่างหากเพราะ==และ!=มีอยู่เป็นตัวดำเนินการที่แตกต่างกัน ในทางกลับกันพวกเขาอาจไม่มีตัวดำเนินการที่แตกต่างกันเนื่องจากคุณสามารถโหลดเกินแยกได้ แต่เนื่องจากเหตุผลดั้งเดิมและความสะดวกสบาย (ความกะทัดรัดของรหัส)
nitro2k01

60

แม้ว่าข้อกังวลของฉันคือทำไมต้องมีคำจำกัดความแยกจากกันสองประการ

คุณไม่จำเป็นต้องกำหนดทั้งสองอย่าง
หากพวกเขาเป็นเอกสิทธิ์เฉพาะบุคคลร่วมกันคุณยังสามารถรัดกุมโดยการกำหนด==และ<ข้างstd :: rel_ops เท่านั้น

cppreference Fom:

#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';
}

มีสถานการณ์ใดบ้างที่เป็นไปได้ที่การถามคำถามเกี่ยวกับวัตถุสองชิ้นที่เท่ากันนั้นสมเหตุสมผล แต่การถามเกี่ยวกับวัตถุที่ไม่เท่ากันนั้นไม่สมเหตุสมผล

เรามักจะเชื่อมโยงผู้ประกอบการเหล่านี้เพื่อความเท่าเทียมกัน
แม้ว่านี่จะเป็นวิธีที่พวกเขาทำงานกับชนิดพื้นฐาน แต่ก็ไม่มีข้อผูกมัดว่าสิ่งนี้จะเป็นพฤติกรรมของพวกเขาในประเภทข้อมูลที่กำหนดเอง คุณไม่จำเป็นต้องส่งคืนบูลหากคุณไม่ต้องการ

ฉันเคยเห็นคนโอเวอร์โหลดโอเปอเรเตอร์ในรูปแบบที่แปลกประหลาดเพียงเพื่อจะพบว่ามันสมเหตุสมผลสำหรับแอปพลิเคชันเฉพาะโดเมนของพวกเขา แม้ว่าอินเทอร์เฟซจะปรากฏขึ้นเพื่อแสดงว่าพวกเขาเป็นเอกสิทธิ์เฉพาะบุคคลผู้เขียนอาจต้องการเพิ่มตรรกะภายในเฉพาะ

(จากมุมมองของผู้ใช้หรือมุมมองของผู้ใช้)

ฉันรู้ว่าคุณต้องการตัวอย่างที่เฉพาะเจาะจง
ดังนั้นนี่คือหนึ่งในกรอบการทดสอบ Catchที่ฉันคิดว่าใช้งานได้จริง:

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 );
}

ตัวดำเนินการเหล่านี้กำลังทำสิ่งที่แตกต่างกันและมันก็ไม่สมเหตุสมผลที่จะกำหนดวิธีหนึ่งให้เป็น! เหตุผลที่ทำเช่นนี้คือเพื่อให้กรอบงานสามารถพิมพ์การเปรียบเทียบที่ทำ ในการดำเนินการดังกล่าวจำเป็นต้องจับภาพบริบทของโอเปอเรเตอร์ที่ใช้งานมากเกินไป


14
โอ้ฉันจะไม่รู้ได้std::rel_opsอย่างไร ขอบคุณมากสำหรับการชี้ว่า
Daniel Jour

5
สำเนาใกล้คำต่อคำจาก cppreference (หรือที่อื่น ๆ ) ควรมีการทำเครื่องหมายอย่างชัดเจนและนำมาประกอบอย่างเหมาะสม rel_opsน่ากลัวอยู่ดี
TC

@TC เห็นด้วยฉันแค่บอกว่ามันเป็นวิธีการที่ OP สามารถทำได้ ฉันไม่รู้วิธีอธิบาย rel_ops ง่ายกว่าตัวอย่างที่แสดง ฉันเชื่อมโยงกับตำแหน่ง แต่มีการโพสต์โค้ดเนื่องจากหน้าอ้างอิงอาจเปลี่ยนแปลงได้ตลอดเวลา
เทรเวอร์ Hickey

4
คุณยังต้องทำให้ชัดเจนว่าตัวอย่างรหัสคือ 99% จาก cppreference แทนที่จะเป็นของคุณเอง
TC

2
Std :: relops ดูเหมือนจะหลุดพ้นจากความโปรดปราน ตรวจสอบตัวเลือกเพิ่มสำหรับสิ่งที่ตรงเป้าหมายมากขึ้น
JDługosz

43

มีบางการประชุมที่ดีขึ้นมากในการที่จะมี(a == b)และ(a != b)มีทั้งที่เป็นเท็จไม่จำเป็นต้องตรงข้าม โดยเฉพาะอย่างยิ่งใน SQL การเปรียบเทียบใด ๆ กับ NULL ให้ผลเป็น NULL ไม่ใช่จริงหรือเท็จ

มันอาจไม่ใช่ความคิดที่ดีที่จะสร้างตัวอย่างใหม่ของสิ่งนี้ถ้าเป็นไปได้เพราะมันไม่ได้ใช้งานง่าย แต่ถ้าคุณกำลังพยายามที่จะสร้างแบบจำลองการประชุมที่มีอยู่ก็ดีที่มีตัวเลือกในการทำให้ผู้ประกอบการ บริบท.


4
ใช้การทำงานแบบ null เช่น SQL ใน C ++? Ewwww แต่ฉันคิดว่ามันไม่ใช่สิ่งที่ฉันคิดว่าควรถูกแบนในภาษา แต่น่ารังเกียจมันอาจจะเป็น

1
@ dan1111 ที่สำคัญยิ่งกว่านั้นบางรสชาติของ SQL อาจถูกเขียนใน c ++ ดังนั้นภาษาจำเป็นต้องรองรับไวยากรณ์ของพวกเขาใช่ไหม?
Joe

1
ถูกต้องฉันถ้าฉันผิดฉันเพิ่งออกจากวิกิพีเดียที่นี่ แต่ไม่ได้เปรียบเทียบกับค่า NULL ในการส่งคืน SQL ไม่ทราบใช่ไม่ใช่เท็จ? และการปฏิเสธของ Unknown ยังไม่เป็นที่รู้จักหรือไม่ ดังนั้นถ้าลอจิก SQL ถูกเข้ารหัสใน C ++, คุณไม่ต้องการNULL == somethingคืนค่า Unknown, และคุณต้องการNULL != somethingคืนค่า Unknown, และคุณต้องการ!UnknownคืนUnknownค่า และในกรณีนั้นการดำเนินการoperator!=ตามการปฏิเสธของoperator==ยังคงถูกต้อง
Benjamin Lindley

1
@Barmar: โอเค แต่แล้วทำไมคำสั่งที่ทำให้"NULL SQL ทำงานด้วยวิธีนี้"ถูกต้อง? หากเรา จำกัด การใช้งานตัวดำเนินการเปรียบเทียบของเรากับการคืนค่าบูลีนนั่นไม่ได้หมายความว่าการใช้ตรรกะ SQL กับตัวดำเนินการเหล่านี้เป็นไปไม่ได้ใช่ไหม
Benjamin Lindley

2
@ บาร์มาร์: ไม่ดีนั่นไม่ใช่ประเด็น OP รู้อยู่แล้วว่าข้อเท็จจริงนั้นหรือคำถามนี้จะไม่มีอยู่จริง จุดที่จะนำเสนอเป็นตัวอย่างที่มันทำให้ความรู้สึกที่ทั้ง 1) การดำเนินการอย่างใดอย่างหนึ่งoperator==หรือoperator!=แต่ไม่ได้อื่น ๆ หรือ 2) ในการดำเนินการในทางอื่นที่ไม่ใช่การปฏิเสธที่operator!= operator==และการใช้ลอจิก SQL สำหรับค่า NULL นั้นไม่ได้เป็นอย่างนั้น
Benjamin Lindley

23

ฉันจะตอบส่วนที่สองของคำถามของคุณเท่านั้น:

หากไม่มีความเป็นไปได้เช่นนั้นเหตุใด C ++ ในโลกจึงให้ผู้ให้บริการสองรายนี้ถูกกำหนดให้เป็นฟังก์ชั่นที่แตกต่างกันสองรายการ

เหตุผลหนึ่งว่าทำไมจึงเหมาะสมที่จะอนุญาตให้นักพัฒนาซอฟต์แวร์โอเวอร์โหลดทั้งคู่คือประสิทธิภาพ คุณอาจจะอนุญาตให้มีการเพิ่มประสิทธิภาพโดยการใช้ทั้งสองและ== !=จากนั้นx != yอาจจะถูกกว่า!(x == y)มี คอมไพเลอร์บางตัวอาจปรับให้เหมาะสมสำหรับคุณ แต่อาจไม่ได้โดยเฉพาะอย่างยิ่งถ้าคุณมีวัตถุที่ซับซ้อนที่มีการแตกแขนงมากมาย

แม้แต่ใน Haskell ที่ซึ่งนักพัฒนาใช้กฎหมายและแนวคิดทางคณิตศาสตร์อย่างจริงจังผู้อื่นก็ยังได้รับอนุญาตให้โอเวอร์โหลดทั้งคู่==และ/=อย่างที่คุณเห็นที่นี่ ( 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'

นี่อาจจะถือเป็นการเพิ่มประสิทธิภาพขนาดเล็ก แต่อาจได้รับการรับประกันสำหรับบางกรณี


3
คลาสตัวหุ้มของ SSE (x86 SIMD) เป็นตัวอย่างที่ดีของสิ่งนี้ มีpcmpeqbคำสั่ง แต่ไม่มีคำสั่งที่เปรียบเทียบกันแล้วสร้างหน้ากาก a! = ดังนั้นหากคุณไม่สามารถย้อนกลับตรรกะของสิ่งที่ใช้ผลลัพธ์คุณต้องใช้คำสั่งอื่นเพื่อกลับด้าน (ข้อเท็จจริงที่สนุกสนาน: ชุดคำสั่ง XOP ของ AMD นั้นมีการเปรียบเทียบneqกันอย่างมาก แต่น่าเสียดายที่ Intel ไม่ได้นำมาใช้ / ขยาย XOP แต่ก็มีคำแนะนำที่เป็นประโยชน์ในส่วนขยาย ISA ที่กำลังจะตาย)
Peter Cordes

1
จุดแรกของ SIMD ในตอนแรกคือประสิทธิภาพการทำงานและโดยทั่วไปคุณมักจะรำคาญที่จะใช้มันด้วยตนเองในลูปที่สำคัญต่อความสมบูรณ์แบบโดยรวม การบันทึกคำสั่งเดียว ( PXORกับทุกคนเพื่อกลับผลลัพธ์การเปรียบเทียบหน้ากาก) ในการวนรอบที่แน่นหนาอาจมีความสำคัญ
Peter Cordes

ผลการดำเนินงานเป็นเหตุผลคือไม่น่าเชื่อถือเมื่อค่าใช้จ่ายเป็นหนึ่งปฏิเสธตรรกะ
ไชโยและ hth - Alf

มันอาจจะมีมากกว่าหนึ่งปฏิเสธตรรกะถ้าการคำนวณค่าใช้จ่ายมากขึ้นอย่างมีนัยสำคัญมากกว่าx == y x != yการคำนวณหลังอาจมีราคาถูกลงอย่างมากเนื่องจากการคาดการณ์ของสาขาเป็นต้น
Centril

16

มีสถานการณ์ใดบ้างที่เป็นไปได้ที่การถามคำถามเกี่ยวกับวัตถุสองชิ้นที่เท่ากันนั้นสมเหตุสมผล แต่การถามเกี่ยวกับวัตถุที่ไม่เท่ากันนั้นไม่สมเหตุสมผล (จากมุมมองของผู้ใช้หรือมุมมองของผู้ใช้)

นั่นเป็นความเห็น อาจจะไม่ แต่นักออกแบบภาษาไม่ใช่ผู้รอบรู้ตัดสินใจที่จะไม่ จำกัด คนที่อาจเกิดสถานการณ์ที่เหมาะสม (อย่างน้อยสำหรับพวกเขา)


13

เพื่อตอบสนองต่อการแก้ไข;

นั่นคือถ้าเป็นไปได้สำหรับบางประเภทที่จะมีตัวดำเนินการ==แต่ไม่ใช่!=หรือในทางกลับกันและเมื่อใดที่เหมาะสม

ในทั่วไปไม่มีก็ไม่ได้ทำให้ความรู้สึก ผู้ประกอบการที่เกี่ยวข้องกับความเสมอภาคและความสัมพันธ์ หากมีความเท่าเทียมกันความไม่เท่าเทียมก็มีเช่นกัน น้อยกว่านั้นมากกว่าและต่อ<=ๆ ไปเป็นต้นด้วยวิธีการที่คล้ายกันนี้ถูกนำไปใช้กับตัวดำเนินการทางคณิตศาสตร์เช่นกัน

นี่เป็นหลักฐานในstd::rel_opsเนมสเปซ หากคุณใช้ความเท่าเทียมกันและน้อยกว่าโอเปอเรเตอร์การใช้เนมสเปซนั้นจะช่วยให้คุณได้รับการนำไปใช้ในแง่ของโอเปอเรเตอร์ที่คุณนำมาใช้จริง

ทุกคนพูดว่ามีเงื่อนไขหรือสถานการณ์ที่คนหนึ่งไม่ได้หมายถึงคนอื่นทันทีหรือไม่สามารถนำไปใช้ในแง่ของคนอื่นได้หรือไม่? ใช่มีเพียงไม่กี่เนื้อหา แต่มีอยู่ อีกครั้งเป็นหลักฐานในrel_opsการเป็น namespace ของมันเอง ด้วยเหตุผลดังกล่าวการอนุญาตให้มีการใช้งานอย่างอิสระช่วยให้คุณสามารถยกระดับภาษาเพื่อให้ได้ความหมายที่คุณต้องการหรือจำเป็นในลักษณะที่ยังคงเป็นธรรมชาติและใช้งานง่ายสำหรับผู้ใช้หรือลูกค้าของรหัส

การประเมินผลที่ขี้เกียจที่กล่าวถึงแล้วเป็นตัวอย่างที่ดีของเรื่องนี้ อีกตัวอย่างที่ดีคือให้พวกเขามีความหมายที่ไม่ได้หมายถึงความเท่าเทียมกันหรือในความเท่าเทียมกันเลย ตัวอย่างที่คล้ายกันนี้เป็นตัวดำเนินการบิตกะ<<และ>>ถูกใช้สำหรับการแทรกและการแยกกระแส แม้ว่ามันอาจจะขมวดคิ้วในแวดวงทั่วไป แต่ในบางพื้นที่มันอาจสมเหตุสมผล


12

หากตัวดำเนินการ==และ!=ตัวดำเนินการไม่ได้หมายความถึงความเท่าเทียมกันในลักษณะเดียวกันกับที่ตัวดำเนินการ<<และตัวส่ง>>กระแสข้อมูลไม่ได้หมายถึงการเลื่อนบิต หากคุณปฏิบัติกับสัญลักษณ์ราวกับว่าพวกเขาหมายถึงแนวคิดอื่น ๆ พวกเขาไม่จำเป็นต้องเป็นเอกสิทธิ์เฉพาะบุคคลร่วมกัน

ในแง่ของความเท่าเทียมกันมันอาจสมเหตุสมผลถ้ากรณีใช้งานของคุณรับประกันว่าวัตถุนั้นไม่สามารถเทียบเคียงได้ดังนั้นการเปรียบเทียบทุกครั้งควรส่งคืนเท็จ (หรือประเภทผลลัพธ์ที่ไม่เทียบเท่าหากผู้ดำเนินการ ฉันไม่สามารถนึกถึงสถานการณ์เฉพาะที่จะรับประกันได้ แต่ฉันเห็นว่ามันสมเหตุสมผลพอ


7

ด้วยพลังอันยิ่งใหญ่มาพร้อมความรับผิดชอบที่ยอดเยี่ยมหรืออย่างน้อยแนวทางที่ดีจริงๆสไตล์

==และ!=สามารถโอเวอร์โหลดเพื่อทำสิ่งที่คุณต้องการ มันเป็นทั้งคำอวยพรและคำสาป ไม่มีการรับประกันว่าไม่ได้หมายถึง!=!(a==b)


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

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

ฉันไม่สามารถแสดงให้เห็นถึงการบรรทุกเกินพิกัดประกอบการนี้ แต่ในตัวอย่างข้างต้นเป็นไปไม่ได้ที่จะกำหนดoperator!=ว่า "ตรงข้าม" operator==ของ



1
@Snowman: Dafang ไม่ได้บอกว่าเป็นการแจกแจงที่ดี (หรือความคิดที่ดีในการกำหนดการแจงนับเช่นนั้น) มันเป็นเพียงตัวอย่างเพื่อแสดงให้เห็นถึงจุด ด้วยวิธีนี้ (อาจจะไม่ดี) นิยามผู้ประกอบการแล้วจะแน่นอนได้หมายความตรงข้ามของ!= ==
AlainD

1
@AlainD คุณคลิกลิงก์ที่ฉันโพสต์และคุณทราบถึงจุดประสงค์ของไซต์นั้นหรือไม่ สิ่งนี้เรียกว่า "อารมณ์ขัน"

1
@Snowman: ฉันทำอย่างแน่นอน ... ขอโทษฉันพลาดมันเป็นลิงค์และตั้งใจประชด! : o)
AlainD

เดี๋ยวก่อนคุณทำงานหนักเกินควร==หรือไม่?
LF

5

ในที่สุดสิ่งที่คุณกำลังตรวจสอบกับผู้ประกอบการเหล่านั้นก็คือการแสดงออกa == bหรือa != bจะส่งกลับค่าบูลีน ( trueหรือfalse) นิพจน์เหล่านี้ส่งคืนค่าบูลีนหลังจากการเปรียบเทียบแทนที่จะเป็นแบบเอกสิทธิ์เฉพาะบุคคล


4

[.. ] ทำไมต้องมีคำจำกัดความแยกกันสองประการ

สิ่งหนึ่งที่ควรพิจารณาคืออาจมีความเป็นไปได้ที่จะใช้ตัวดำเนินการอย่างใดอย่างหนึ่งอย่างมีประสิทธิภาพมากกว่าแค่ใช้การปฏิเสธของอีกฝ่าย

(ตัวอย่างของฉันที่นี่เป็นขยะ แต่ประเด็นยังคงอยู่ลองนึกถึงตัวกรองที่มีลูกเล่นเช่น: อนุญาตให้ทำการทดสอบได้อย่างรวดเร็วหากมีบางอย่างไม่อยู่ในชุด แต่การทดสอบว่าอาจใช้เวลานานกว่านี้)

[ .. ] โดยความหมายคือa != b!(a == b)

และมันเป็นความรับผิดชอบของคุณในฐานะโปรแกรมเมอร์ที่จะทำสิ่งนั้น อาจเป็นสิ่งที่ดีในการเขียนทดสอบ


4
วิธีการที่ไม่!((a == rhs.a) && (b == rhs.b))ได้รับอนุญาตให้ไฟฟ้าลัดวงจร? ถ้า!(a == rhs.a)ไปแล้ว(b == rhs.b)จะไม่ได้รับการประเมิน
Benjamin Lindley

นี่เป็นตัวอย่างที่ไม่ดี การลัดวงจรไม่เพิ่มความได้เปรียบใด ๆ
Oliver Charlesworth

@Oliver Charlesworth Alone มันไม่ได้ แต่เมื่อรวมเข้ากับโอเปอเรเตอร์ที่แยกต่างหากมันทำ: ในกรณีของ==มันจะหยุดเปรียบเทียบทันทีที่องค์ประกอบที่เกี่ยวข้องแรกไม่เท่ากัน แต่ในกรณีของ!=ถ้ามันถูกนำมาใช้ในแง่ของ==มันจะต้องเปรียบเทียบองค์ประกอบที่เกี่ยวข้องทั้งหมดก่อน (เมื่อพวกเขาเท่าเทียมกันทั้งหมด) เพื่อให้สามารถบอกได้ว่าพวกเขาไม่ได้ไม่เท่ากัน: P แต่เมื่อดำเนินการตาม ตัวอย่างด้านบนจะหยุดเปรียบเทียบทันทีที่พบคู่ที่ไม่เท่ากันอันแรก ตัวอย่างที่ดีแน่นอน
BarbaraKwarc

@BenjaminLindley True ตัวอย่างของฉันไร้สาระสมบูรณ์ น่าเสียดายที่ฉันไม่สามารถพบกับตู้เอทีเอ็มได้อีกมันสายเกินไปที่นี่
Daniel Jour

1
@BarbaraKwarc: !((a == b) && (c == d))และ(a != b) || (c != d)เทียบเท่าในแง่ของประสิทธิภาพการลัดวงจร
Oliver Charlesworth

2

ด้วยการปรับแต่งพฤติกรรมของผู้ปฏิบัติงานคุณสามารถทำให้พวกเขาทำในสิ่งที่คุณต้องการ

คุณอาจต้องการปรับแต่งสิ่งต่าง ๆ ตัวอย่างเช่นคุณอาจต้องการปรับแต่งชั้นเรียน วัตถุของคลาสนี้สามารถเปรียบเทียบได้เพียงแค่ตรวจสอบคุณสมบัติเฉพาะ เมื่อรู้ว่าเป็นกรณีนี้คุณสามารถเขียนโค้ดเฉพาะที่ตรวจสอบสิ่งที่น้อยที่สุดแทนการตรวจสอบทุก ๆ บิตของคุณสมบัติเดียวในวัตถุทั้งหมด

ลองจินตนาการถึงกรณีที่คุณสามารถเข้าใจได้ว่ามีบางอย่างที่แตกต่างออกไปอย่างรวดเร็วถ้าไม่เร็วกว่าที่คุณจะพบว่ามีบางอย่างเหมือนกัน จริงอยู่ที่คุณทราบว่ามีบางอย่างเหมือนกันหรือแตกต่างจากนั้นคุณสามารถรู้สิ่งตรงกันข้ามได้ง่ายๆโดยการพลิกเล็กน้อย อย่างไรก็ตามการเปิดบิตนั้นเป็นการดำเนินการเพิ่มเติม ในบางกรณีเมื่อโค้ดถูกเรียกใช้ซ้ำจำนวนมากการบันทึกหนึ่งการดำเนินการ (คูณด้วยหลายครั้ง) สามารถเพิ่มความเร็วโดยรวมได้ (ตัวอย่างเช่นหากคุณบันทึกการทำงานหนึ่งรายการต่อพิกเซลของหน้าจอล้านพิกเซลคุณเพียงบันทึกการดำเนินงานหนึ่งล้านรายการคูณด้วย 60 หน้าจอต่อวินาทีและคุณบันทึกการทำงานได้มากขึ้น)

คำตอบของ hvdให้ตัวอย่างเพิ่มเติมบางอย่าง


2

ใช่เพราะอย่างใดอย่างหนึ่งหมายถึง "เทียบเท่า" และอีกวิธีหนึ่งหมายถึง "ไม่เทียบเท่า" และข้อกำหนดนี้เป็นพิเศษร่วมกัน ความหมายอื่นใดสำหรับโอเปอเรเตอร์นี้คือทำให้สับสนและควรหลีกเลี่ยงโดยวิธีการทั้งหมด


พวกเขาไม่ได้ร่วมกันเฉพาะสำหรับทุกกรณี ตัวอย่างเช่นสองอนันต์ทั้งสองไม่เท่ากับกันและไม่เท่ากับกันและกัน
vladon

@vladon สามารถใช้ใช้แทนตัวอื่นในกรณีทั่วไปได้หรือไม่ ไม่นั่นหมายความว่าพวกเขาไม่เท่าเทียมกัน ส่วนที่เหลือทั้งหมดจะไปที่ฟังก์ชั่นพิเศษแทนที่จะใช้โอเปอเรเตอร์ == /! =
oliora

@ vladon โปรดแทนทุกกรณีทั่วไปอ่านทุกกรณีในคำตอบของฉัน
oliora

@ vladon มากเท่านี้จะเป็นจริงในวิชาคณิตศาสตร์คุณสามารถยกตัวอย่างที่a != bไม่เท่ากับ!(a == b)เหตุผลนี้ใน C หรือไม่?
nitro2k01

2

อาจเป็นกฎที่ไม่มีใครเทียบได้ซึ่งa != bเป็นเท็จและa == bเป็นเท็จเหมือนบิตไร้สัญชาติ

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

หากคุณต้องการจัดเรียงสัญลักษณ์เชิงตรรกะใหม่! ([A] || [B]) จะกลายเป็นตรรกะ ([! A] & [! B])
Thijser

โปรดทราบว่าประเภทกลับของoperator==()และoperator!=()ไม่จำเป็นต้องboolพวกเขาอาจจะมี enum ที่รวมถึงการไร้สัญชาติถ้าคุณอยากที่และยังผู้ประกอบการอาจจะยังคงกำหนดไว้เพื่อ(a != b) == !(a==b)ถือ ..
lorro
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.