सी ++ 20 शुरू की डिफ़ॉल्ट तुलना, उर्फ "अंतरिक्ष यान"operator<=>
, जो आप अनुरोध करने की अनुमति देता संकलक उत्पन्न <
/ <=
/ ==
/ !=
/ >=
/ और / या >
स्पष्ट / भोली (?) कार्यान्वयन के साथ ऑपरेटरों ...
auto operator<=>(const MyClass&) const = default;
... लेकिन आप इसे और अधिक जटिल स्थितियों (नीचे चर्चा की गई) के लिए अनुकूलित कर सकते हैं। भाषा के प्रस्ताव के लिए यहां देखें , जिसमें औचित्य और चर्चा है। यह उत्तर C ++ 17 और पहले के लिए प्रासंगिक है, और अंतर्दृष्टि के लिए जब आपको कार्यान्वयन को अनुकूलित करना चाहिए operator<=>
...।
ऐसा प्रतीत हो सकता है कि यह C ++ का पहले से मानकीकृत नहीं है, लेकिन अक्सर स्ट्रक्चर्स / क्लासेस में कुछ डेटा सदस्यों की तुलना (जैसे काउंटर, कैश्ड परिणाम, कंटेनर क्षमता, अंतिम ऑपरेशन सफलता / त्रुटि कोड, कर्सर) के रूप में किया जाता है। साथ ही असंख्य चीजों के बारे में निर्णय लेने सहित
- कौन से क्षेत्र पहले तुलना करने के लिए, जैसे किसी विशेष
int
सदस्य की तुलना करना 99% असमान वस्तुओं को बहुत जल्दी खत्म कर सकता है, जबकि एक map<string,string>
सदस्य के पास अक्सर समान प्रविष्टियां हो सकती हैं और तुलना करने के लिए अपेक्षाकृत महंगी हो सकती हैं - यदि मान रनटाइम पर लोड किए जाते हैं, तो प्रोग्रामर के पास अंतर्दृष्टि हो सकती है संकलक संभवतः नहीं कर सकता
- तार की तुलना में: केस सेंसिटिविटी, व्हॉट्सएप और सेपरेटर की समानता, सम्मेलनों से बचना ...
- तैरने / डबल्स की तुलना करते समय परिशुद्धता
- क्या NaN फ्लोटिंग पॉइंट वैल्यू को समान माना जाना चाहिए
- पॉइंटर्स या पॉइंट-टू-डेटा की तुलना करना (और यदि बाद वाला है, तो कैसे पता चले कि पॉइंटर्स एरे करने के लिए हैं और कितने ऑब्जेक्ट्स / बाइट्स की तुलना की आवश्यकता है)
- चाहे आदेश मामलों जब अवर्गीकृत कंटेनरों की तुलना (जैसे
vector
, list
), और इसलिए चाहे वह तरह temporaries को हर बार एक तुलना किया जाता है अतिरिक्त स्मृति का उपयोग बनाम की तुलना से पहले में जगह उन्हें सुलझाने के लिए ठीक है, तो
- कितने सरणी तत्व वर्तमान में मान्य मान रखते हैं जिनकी तुलना की जानी चाहिए (कहीं आकार है या सेंटिनल?)
union
तुलना करने के लिए कौन सा सदस्य है
- सामान्यीकरण: उदाहरण के लिए, तिथि प्रकार महीने के महीने या महीने के बाहर की अनुमति दे सकते हैं, या एक तर्कसंगत / अंश वस्तु में 6/8 वाँ भाग हो सकता है, जबकि दूसरे में 3 / 4ers होते हैं, जो प्रदर्शन के कारणों से सही होते हैं एक अलग सामान्यीकरण कदम के साथ आलसी; आपको यह तय करना पड़ सकता है कि तुलना से पहले एक सामान्यीकरण को ट्रिगर करना है या नहीं
- क्या करें जब कमजोर संकेत मान्य नहीं हैं
- उन सदस्यों और ठिकानों को कैसे संभाला जाए जो
operator==
खुद को लागू नहीं करते (लेकिन हो सकता है compare()
या operator<
या हो सकता है str()
...)
- अन्य थ्रेड्स को अद्यतन करने के लिए इच्छित डेटा को पढ़ते / तुलना करते समय ताले को क्या लेना चाहिए
इसलिए, जब तक आपने स्पष्ट रूप से यह नहीं सोचा कि आपकी विशिष्ट संरचना के लिए इसका क्या अर्थ है, बजाय इसे संकलित करने के , बल्कि आपको रन-टाइम पर एक सार्थक परिणाम नहीं देना है , तो यह एक तरह की त्रुटि है ।
उस सब ने कहा, यह अच्छा होगा यदि C ++ आपको यह बताता है कि bool operator==() const = default;
आपने "भोले" सदस्य-द्वारा-सदस्यीय ==
परीक्षण ठीक किया था या नहीं । उसी के लिए !=
। यह देखते हुए कई सदस्यों / अड्डों, "डिफ़ॉल्ट" <
, <=
, >
, और >=
कार्यान्वयन हालांकि निराशाजनक लगता है - घोषणा के संभव है, लेकिन बहुत होने के लिए क्या चाहता है, सदस्य आदेश देने के लिए अनिवार्यता परस्पर विरोधी (अड्डों के सदस्यों से पहले जरूरी जा रहा है, के द्वारा समूहीकरण दी संभावना नहीं के आदेश के आधार पर व्यापक उपयोग, निर्माण / आश्रित उपयोग से पहले विनाश)। अधिक व्यापक रूप से उपयोगी होने के लिए, C ++ को विकल्पों का मार्गदर्शन करने के लिए एक नए डेटा सदस्य / आधार एनोटेशन सिस्टम की आवश्यकता होगी - जो कि मानक में होने के लिए एक बहुत अच्छी बात होगी, हालांकि आदर्श रूप से एएसटी-आधारित उपयोगकर्ता-परिभाषित कोड पीढ़ी के साथ मिलकर ... मुझे उम्मीद है यह
समानता ऑपरेटरों का विशिष्ट कार्यान्वयन
एक प्रशंसनीय कार्यान्वयन
यह संभावना है कि एक उचित और कुशल कार्यान्वयन होगा:
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);
}
// ...etc...
जब आप "स्वयं" (यानी कॉर्पोरेट और 3 पार्टी के लिबास वाला एक कारक संपादित कर सकते हैं) जिस वर्ग की आप तुलना करना चाहते हैं, और विशेष रूप से C ++ 14 की तैयारियों के साथ फ़ंक्शन रिटर्न प्रकार को return
स्टेटमेंट से हटाने के लिए, यह अक्सर "जोड़ने के लिए अच्छा है" आप जिस वर्ग की तुलना करना चाहते हैं, उसके लिए "सदस्य फ़ंक्शन को बाँधें:"
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)
... तो यह एक ला इस्तेमाल किया जा सकता है ...
#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)
(C ++ 14 सदस्य टाई संस्करण यहाँ )
अपने MyStruct1 की बारीकियों की चर्चा
चुनाव के लिए स्वतंत्र बनाम सदस्य प्रदान करने के निहितार्थ हैं operator==()
...
फ्रीस्टैंडिंग कार्यान्वयन
आपके पास बनाने का एक दिलचस्प फैसला है। जैसा कि आपकी कक्षा को एक से निर्मित किया जा सकता है 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
के my_struct_2
सदस्य! इसके अलावा, एक अस्थायी निर्माण एक MyStruct1
बहुत ही अकुशल ऑपरेशन हो सकता है, क्योंकि इसमें मौजूदा my_struct2
सदस्य को एक अस्थायी में कॉपी करना शामिल है , केवल तुलना के बाद इसे फेंकने के लिए। (निश्चित रूप से, आप उस निर्माता को MyStruct1
तुलना करके explicit
या उसके लिए डिफ़ॉल्ट मान हटाकर s के इस निहित निर्माण को रोक सकते हैं an_int
।)
सदस्य कार्यान्वयन
यदि आप a MyStruct1
से निहित निर्माण से बचना चाहते हैं MyStruct2
, तो तुलना ऑपरेटर को सदस्य कार्य करें:
struct MyStruct1
{
...
bool operator==(const MyStruct1& rhs) const
{
return tie() == rhs.tie(); // or another approach as above
}
};
const
कीवर्ड पर ध्यान दें - केवल सदस्य कार्यान्वयन के लिए आवश्यक - संकलक को सलाह देता है कि वस्तुओं की तुलना करना उन्हें संशोधित नहीं करता है, इसलिए const
वस्तुओं पर अनुमति दी जा सकती है ।
दृश्य अभ्यावेदन की तुलना करना
कभी-कभी आप जिस तरह की तुलना करना चाहते हैं, उसका सबसे आसान तरीका हो सकता है ...
return lhs.to_string() == rhs.to_string();
... जो अक्सर बहुत महंगा होता है - वे string
दर्दनाक तरीके से सिर्फ फेंकने के लिए बनाए जाते हैं! फ्लोटिंग पॉइंट वैल्यू वाले प्रकारों के लिए, दृश्य प्रतिनिधित्वों की तुलना करने का मतलब है कि प्रदर्शित अंकों की संख्या उस सहिष्णुता को निर्धारित करती है जिसके भीतर लगभग-समान मूल्यों को तुलना के दौरान समान माना जाता है।
struct
समानता के लिए अपनी तुलना कैसे करना चाहते हैं ? और अगर आप सरल तरीका चाहते हैं,memcmp
तो हमेशा लंबे समय तक आपकी संरचना में सूचक नहीं होते हैं।