C ++ 20 แนะนำการเปรียบเทียบเริ่มต้นหรือที่เรียกว่า "ยานอวกาศ"operator<=>
ซึ่งช่วยให้คุณสามารถร้องขอ<
/ <=
/ ==
/ !=
/ >=
/ และ / หรือ>
ตัวดำเนินการที่สร้างโดยคอมไพเลอร์ด้วยการใช้งานที่ชัดเจน / ไร้เดียงสา (?) ...
auto operator<=>(const MyClass&) const = default;
... แต่คุณสามารถปรับแต่งสำหรับสถานการณ์ที่ซับซ้อนมากขึ้นได้ (อธิบายไว้ด้านล่าง) ดูข้อเสนอด้านภาษาที่นี่ซึ่งมีเหตุผลและการอภิปราย คำตอบนี้ยังคงเกี่ยวข้องกับ C ++ 17 และรุ่นก่อนหน้าและสำหรับข้อมูลเชิงลึกว่าคุณควรปรับแต่งการใช้งานเมื่อใดoperator<=>
...
อาจดูเหมือนไม่เป็นประโยชน์เล็กน้อยที่ C ++ ไม่ได้กำหนดมาตรฐานไว้ก่อนหน้านี้ แต่บ่อยครั้งโครงสร้าง / คลาสจะมีสมาชิกข้อมูลบางส่วนที่จะแยกออกจากการเปรียบเทียบ (เช่นตัวนับ, ผลลัพธ์ที่แคช, ความจุของคอนเทนเนอร์, ความสำเร็จของการดำเนินการครั้งสุดท้าย / รหัสข้อผิดพลาด, เคอร์เซอร์) เช่น ตลอดจนการตัดสินใจเกี่ยวกับสิ่งต่างๆมากมายรวมถึง แต่ไม่ จำกัด เพียง:
- ฟิลด์ใดที่จะเปรียบเทียบก่อนเช่นการเปรียบเทียบ
int
สมาชิกโดยเฉพาะอาจกำจัด 99% ของวัตถุที่ไม่เท่ากันได้อย่างรวดเร็วในขณะที่กmap<string,string>
สมาชิกมักมีรายการที่เหมือนกันและมีราคาค่อนข้างแพงในการเปรียบเทียบ - หากโหลดค่าที่รันไทม์โปรแกรมเมอร์อาจมีข้อมูลเชิงลึก คอมไพเลอร์ไม่สามารถเป็นไปได้
- ในการเปรียบเทียบสตริง: ความไวของตัวพิมพ์เล็กและตัวพิมพ์ใหญ่, ความเท่ากันของช่องว่างและตัวคั่น, การหลีกเลี่ยงอนุสัญญา ...
- ความแม่นยำเมื่อเปรียบเทียบลอย / คู่
- ว่าค่าจุดลอยตัวของ NaN ควรถือว่าเท่ากันหรือไม่
- การเปรียบเทียบพอยน์เตอร์หรือชี้ไปยังข้อมูล (และถ้าเป็นอย่างหลังจะรู้ได้อย่างไรว่าพอยน์เตอร์เป็นอาร์เรย์และจำนวนอ็อบเจ็กต์ / ไบต์ที่ต้องการการเปรียบเทียบ)
- คำสั่งซื้อมีความสำคัญหรือไม่เมื่อเปรียบเทียบคอนเทนเนอร์ที่ไม่ได้เรียงลำดับ (เช่น
vector
,list
) และถ้าเป็นเช่นนั้นไม่ว่าจะเป็น OK เพื่อจัดเรียงพวกเขาในสถานที่ก่อนที่จะเปรียบเทียบกับการใช้หน่วยความจำเสริมการชั่วคราวเรียงลำดับในแต่ละครั้งการเปรียบเทียบที่จะทำ
- ในปัจจุบันมีองค์ประกอบอาร์เรย์จำนวนเท่าใดที่มีค่าที่ถูกต้องซึ่งควรนำมาเปรียบเทียบ (มีขนาดอยู่ที่ใดที่หนึ่งหรือมีแมวมอง?)
- สมาชิกคนไหนของ
union
จะเปรียบเทียบ
- การทำให้เป็นมาตรฐาน: ตัวอย่างเช่นประเภทวันที่อาจอนุญาตให้อยู่นอกช่วงของวันของเดือนหรือเดือนของปีหรือวัตถุที่มีเหตุผล / เศษส่วนอาจมี 6 / 8th ในขณะที่อีกประเภทหนึ่งมี 3 / 4ers ซึ่งด้วยเหตุผลด้านประสิทธิภาพจึงถูกต้อง อย่างเกียจคร้านกับขั้นตอนการทำให้เป็นมาตรฐานแยกต่างหาก คุณอาจต้องตัดสินใจว่าจะเริ่มการทำให้เป็นมาตรฐานก่อนการเปรียบเทียบหรือไม่
- จะทำอย่างไรเมื่อจุดอ่อนไม่ถูกต้อง
- วิธีจัดการกับสมาชิกและฐานที่ไม่ได้ใช้
operator==
เอง (แต่อาจมีcompare()
หรือoperator<
หรือstr()
หรือได้รับ ... )
- สิ่งที่ต้องใช้ในขณะที่อ่าน / เปรียบเทียบข้อมูลที่เธรดอื่นอาจต้องการอัปเดต
ดังนั้นจึงเป็นเรื่องดีที่จะมีข้อผิดพลาดจนกว่าคุณจะคิดอย่างชัดเจนว่าการเปรียบเทียบควรมีความหมายอย่างไรกับโครงสร้างเฉพาะของคุณแทนที่จะปล่อยให้รวบรวม แต่ไม่ให้ผลลัพธ์ที่มีความหมายในขณะทำงานแต่ไม่ให้ผลที่มีความหมายที่ใช้เวลา
ทั้งหมดที่กล่าวมาคงจะดีถ้า C ++ ให้คุณพูดbool operator==() const = default;
เมื่อคุณตัดสินใจว่าจะ==
ทำการทดสอบแบบสมาชิกโดยสมาชิกแบบ"ไร้เดียงสา" ก็โอเค เหมือนกันสำหรับ!=
. ป.ร. ให้สมาชิกหลาย / เบส, "เริ่มต้น" <
, <=
, >
และ>=
การใช้งานดูเหมือนสิ้นหวังแม้ว่า - ซ้อนบนพื้นฐานของคำสั่งของการประกาศของไปได้ แต่ไม่น่าจะเป็นสิ่งที่อยากให้ความขัดแย้งตอบสนองความต้องการสำหรับการสั่งซื้อสมาชิก (ฐานเป็นจำเป็นต้องก่อนที่สมาชิกในการจัดกลุ่มโดย ความสามารถในการเข้าถึงการก่อสร้าง / การทำลายก่อนการใช้งาน) เพื่อให้เป็นประโยชน์อย่างกว้างขวางมากขึ้น C ++ จำเป็นต้องมีระบบคำอธิบายประกอบข้อมูลสมาชิก / ฐานข้อมูลใหม่เพื่อเป็นแนวทางในการเลือกซึ่งจะเป็นสิ่งที่ดีที่จะมีในมาตรฐานแม้ว่าจะควบคู่ไปกับการสร้างรหัสที่ผู้ใช้กำหนดโดย AST ... ฉันคาดหวัง มัน'
การใช้ตัวดำเนินการความเท่าเทียมกันโดยทั่วไป
การดำเนินการที่เป็นไปได้
เป็นไปได้ว่าการใช้งานที่สมเหตุสมผลและมีประสิทธิภาพจะเป็นดังนี้:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.my_struct2 == rhs.my_struct2 &&
lhs.an_int == rhs.an_int;
}
โปรดทราบว่าสิ่งนี้ต้องการoperator==
สำหรับMyStruct2
เช่นกัน
ผลกระทบของการใช้งานนี้และทางเลือกอื่นจะกล่าวถึงภายใต้หัวข้อการอภิปรายเกี่ยวกับข้อมูลเฉพาะของ MyStruct1 ของคุณด้านล่าง
แนวทางที่สอดคล้องกับ ==, <,> <= ฯลฯ
เป็นเรื่องง่ายที่จะใช้ประโยชน์จากstd::tuple
ตัวดำเนินการเปรียบเทียบเพื่อเปรียบเทียบอินสแตนซ์คลาสของคุณเอง - เพียงแค่ใช้std::tie
เพื่อสร้างการอ้างอิงไปยังฟิลด์ตามลำดับการเปรียบเทียบที่ต้องการ สรุปตัวอย่างของฉันจากที่นี่ :
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);
}
เมื่อคุณ "เป็นเจ้าของ" (เช่นสามารถแก้ไขปัจจัยที่มี libs ขององค์กรและบุคคลที่สาม) คลาสที่คุณต้องการเปรียบเทียบและโดยเฉพาะอย่างยิ่งกับการเตรียมพร้อมของ C ++ 14 ในการอนุมานประเภทการส่งคืนฟังก์ชันจากreturn
คำสั่งมักจะดีกว่าที่จะเพิ่ม " tie "ฟังก์ชันสมาชิกกับคลาสที่คุณต้องการเปรียบเทียบ:
auto tie() const { return std::tie(my_struct1, an_int); }
จากนั้นการเปรียบเทียบด้านบนทำให้ง่ายขึ้นเพื่อ:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.tie() == rhs.tie();
}
หากคุณต้องการชุดตัวดำเนินการเปรียบเทียบที่สมบูรณ์กว่านี้ฉันขอแนะนำตัวดำเนินการเพิ่ม (ค้นหาless_than_comparable
) หากไม่เหมาะสมด้วยเหตุผลบางประการคุณอาจหรือไม่ชอบแนวคิดของมาโครการสนับสนุน(ออนไลน์) :
#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)
... ที่สามารถใช้ a la ...
#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)
(เวอร์ชันผูกสมาชิก C ++ 14 ที่นี่ )
การอภิปรายเฉพาะของ MyStruct1 ของคุณ
มีผลกระทบต่อการเลือกให้สมาชิกยืนฟรีกับสมาชิกoperator==()
...
การใช้งานอิสระ
คุณมีการตัดสินใจที่น่าสนใจ เนื่องจากชั้นเรียนของคุณสามารถสร้างขึ้นโดยปริยายจาก a ฟังก์ชันMyStruct2
ยืนอิสระ / ไม่เป็นสมาชิกbool operator==(const MyStruct2& lhs, const MyStruct2& rhs)
จะรองรับ ...
my_MyStruct2 == my_MyStruct1
... โดยการสร้างชั่วคราวก่อนMyStruct1
จากmy_myStruct2
นั้นทำการเปรียบเทียบ นี้แน่นอนจะออกตั้งค่าพารามิเตอร์คอนสตรัคที่เริ่มต้นของMyStruct1::an_int
-1
ทั้งนี้ขึ้นอยู่กับว่าคุณรวมถึงan_int
การเปรียบเทียบในการดำเนินการของคุณoperator==
ที่MyStruct1
อาจจะหรืออาจจะไม่เปรียบเทียบเท่ากับMyStruct2
ว่าตัวเองเปรียบเทียบเท่ากับMyStruct1
's my_struct_2
สมาชิก! นอกจากนี้การสร้างชั่วคราวMyStruct1
อาจเป็นการดำเนินการที่ไม่มีประสิทธิภาพมากเนื่องจากเกี่ยวข้องกับการคัดลอกmy_struct2
สมาชิกที่มีอยู่ไปไว้ที่ชั่วคราวเพียงเพื่อทิ้งหลังจากการเปรียบเทียบ (แน่นอนคุณสามารถป้องกันการสร้างMyStruct1
s โดยนัยนี้เพื่อเปรียบเทียบได้โดยการสร้างตัวสร้างนั้นexplicit
หรือลบค่าเริ่มต้นสำหรับan_int
)
การใช้งานสมาชิก
หากคุณต้องการหลีกเลี่ยงการสร้าง a โดยนัยMyStruct1
จาก a MyStruct2
ให้ทำให้ตัวดำเนินการเปรียบเทียบเป็นฟังก์ชันสมาชิก:
struct MyStruct1
{
...
bool operator==(const MyStruct1& rhs) const
{
return tie() == rhs.tie();
}
};
หมายเหตุconst
คีย์เวิร์ด - จำเป็นสำหรับการใช้งานสมาชิกเท่านั้น - แนะนำคอมไพเลอร์ว่าการเปรียบเทียบอ็อบเจ็กต์ไม่ได้แก้ไขดังนั้นจึงอนุญาตให้ใช้const
อ็อบเจ็กต์ได้
การเปรียบเทียบการแสดงที่มองเห็นได้
บางครั้งวิธีที่ง่ายที่สุดในการเปรียบเทียบแบบที่คุณต้องการก็คือ ...
return lhs.to_string() == rhs.to_string();
... ซึ่งมักมีราคาแพงมากเช่นกัน - สิ่งเหล่าstring
นี้สร้างขึ้นอย่างเจ็บปวดเพียงเพื่อจะถูกโยนทิ้ง! สำหรับประเภทที่มีค่าทศนิยมการเปรียบเทียบการแทนค่าที่มองเห็นได้หมายถึงจำนวนของตัวเลขที่แสดงจะเป็นตัวกำหนดความอดทนซึ่งค่าที่เกือบเท่ากันจะถือว่าเท่ากันในระหว่างการเปรียบเทียบ
struct
ความเท่าเทียมกันอย่างไร? และถ้าคุณต้องการวิธีง่ายๆก็มักจะmemcmp
มีโครงสร้างของคุณที่ไม่มีตัวชี้อยู่นาน