C ++ में स्ट्रक्चर्स की तुलना करते समय कोई == ऑपरेटर नहीं मिला


96

निम्नलिखित संरचना के दो उदाहरणों की तुलना में, मुझे एक त्रुटि प्राप्त होती है:

struct MyStruct1 {
    MyStruct1(const MyStruct2 &_my_struct_2, const int _an_int = -1) :
        my_struct_2(_my_struct_2),
        an_int(_an_int)
    {}

    std::string toString() const;

    MyStruct2 my_struct_2;
    int an_int;
};

त्रुटि है:

त्रुटि C2678: बाइनरी '==': कोई ऑपरेटर नहीं पाया गया जो 'myproj :: MyStruct1' प्रकार का बाएं हाथ का ऑपरेंड लेता है (या कोई स्वीकार्य रूपांतरण नहीं है)

क्यों?

जवाबों:


126

C ++ में, structडिफ़ॉल्ट रूप से उत्पन्न तुलना ऑपरेटर नहीं है। आपको अपना लिखने की आवश्यकता है:

bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return /* your comparison code goes here */
}

21
@ जोनाथन: C ++ को पता होगा कि आप structसमानता के लिए अपनी तुलना कैसे करना चाहते हैं ? और अगर आप सरल तरीका चाहते हैं, memcmpतो हमेशा लंबे समय तक आपकी संरचना में सूचक नहीं होते हैं।
Xio

12
@ Xeo: memcmpगैर-पीओडी सदस्यों (जैसे std::string) और गद्देदार संरचनाओं के साथ विफल रहता है ।
fredoverflow

16
@ जोनाथन "आधुनिक" भाषाएं मुझे पता है कि एक ==ऑपरेटर प्रदान करता है --- एक शब्दार्थ के साथ जो लगभग कभी नहीं चाहता है। (और वे इसे ओवरराइड करने का साधन प्रदान नहीं करते हैं, इसलिए आप एक सदस्य फ़ंक्शन का उपयोग करने के लिए समाप्त हो जाते हैं)। मुझे पता है कि "आधुनिक" भाषाएं भी मूल्य शब्दार्थ प्रदान नहीं करती हैं, इसलिए आप उपयुक्त नहीं होने पर भी पॉइंटर्स का उपयोग करने के लिए मजबूर होते हैं।
जेम्स कांजे

4
@ जोनाथन मामले निश्चित रूप से भिन्न होते हैं, यहां तक ​​कि किसी दिए गए कार्यक्रम के भीतर भी। इकाई ऑब्जेक्ट्स के लिए, जावा द्वारा प्रदान किया गया समाधान बहुत अच्छी तरह से काम करता है (और निश्चित रूप से, आप सी ++ में एक ही काम कर सकते हैं --- यह इकाई वस्तुओं के लिए भी मुहावरेदार सी ++ है)। सवाल यह है कि मूल्य वस्तुओं के बारे में क्या करना है। C operator=संगतता के कारणों के लिए C ++ एक डिफ़ॉल्ट प्रदान करता है (भले ही वह अक्सर गलत काम करता है)। C संगतता की आवश्यकता नहीं है operator==, हालाँकि। विश्व स्तर पर, मैं पसंद करता हूँ कि C ++ जावा क्या करता है। (मैं सी # नहीं जानता, इसलिए शायद यह बेहतर है।)
जेम्स कांज़े

9
कम से कम यह संभव होना चाहिए= default !
user362515

93

सी ++ 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दर्दनाक तरीके से सिर्फ फेंकने के लिए बनाए जाते हैं! फ्लोटिंग पॉइंट वैल्यू वाले प्रकारों के लिए, दृश्य प्रतिनिधित्वों की तुलना करने का मतलब है कि प्रदर्शित अंकों की संख्या उस सहिष्णुता को निर्धारित करती है जिसके भीतर लगभग-समान मूल्यों को तुलना के दौरान समान माना जाता है।


ठीक है, वास्तव में तुलना ऑपरेटरों के लिए <,>, <=,> = यह केवल लागू करने के लिए आवश्यक होना चाहिए <। बाकी का अनुसरण करता है, और उन्हें लागू करने से अलग कुछ भी अर्थपूर्ण तरीके से लागू करने का कोई सार्थक तरीका नहीं है जो स्वचालित रूप से उत्पन्न हो सकता है। यह विचित्र है कि आपको उन सभी को स्वयं लागू करना होगा।
आंद्रे

@ आन्द्रे: अधिक बार एक मैन्युअल रूप से लिखा int cmp(x, y)या compareसमारोह के लिए एक नकारात्मक के लिए मूल्य लौटने x < yऔर समानता के लिए, 0 के लिए एक सकारात्मक मूल्य x > yके लिए आधार के रूप में प्रयोग किया जाता है <, >, <=, >=, ==, और !=; उन सभी ऑपरेटरों को एक वर्ग में इंजेक्ट करने के लिए CRTP का उपयोग करना बहुत आसान है। मुझे यकीन है कि मैंने एक पुराने उत्तर में कार्यान्वयन पोस्ट किया है, लेकिन इसे जल्दी से नहीं मिला।
टोनी डेलरो

@TonyD सुनिश्चित करें कि आप ऐसा कर सकते हैं, लेकिन इसे लागू करना आसान है >, <=और >=के संदर्भ में <। आप इसे लागू भी कर सकते हैं ==और !=उस तरह से, लेकिन यह आमतौर पर एक बहुत कुशल कार्यान्वयन नहीं होगा जो मुझे लगता है। यह अच्छा होगा यदि इस सब के लिए कोई CRTP या अन्य ट्रिक्स की आवश्यकता नहीं होगी, लेकिन मानक इन ऑपरेटरों के ऑटो-जनरेट को अनिवार्य करेगा यदि उपयोगकर्ता द्वारा स्पष्ट रूप से परिभाषित नहीं किया गया है और <परिभाषित किया गया है।
एंड्रे

@ आन्द्रे: क्योंकि यह है ==और !=कुशलता से उपयोग करते हुए व्यक्त नहीं किया जा सकता है <कि सब कुछ सामान्य है के लिए की तुलना का उपयोग कर। "यह अच्छा होगा कोई CRTP या अन्य चाल की जरूरत होगी, तो" - शायद, लेकिन फिर CRTP आसानी से अन्य ऑपरेटरों के बहुत सारे (उत्पन्न करने के लिए इस्तेमाल किया जा सकता जैसे बिटवाइज़ |, &, ^से |=, &=और ^=, + - * / %उनके काम के रूपों से; बाइनरी -एकल निषेध से और +) - इस विषय पर इतने सारे संभावित रूपांतर उपयोगी हैं कि सिर्फ एक सुंदर मनमाना टुकड़ा के लिए एक भाषा सुविधा प्रदान करना विशेष रूप से सुरुचिपूर्ण नहीं है।
टोनी डेलारॉय

क्या आप एक प्रशंसनीय क्रियान्वयन के लिए एक संस्करण जोड़ना चाहेंगे std::tieजो कई सदस्यों की तुलना करने के लिए उपयोग करता है?
नाथनऑलिवर

17

आप स्पष्ट रूप से परिभाषित करने की जरूरत operator ==के लिए MyStruct1

struct MyStruct1 {
  bool operator == (const MyStruct1 &rhs) const
  { /* your logic for comparision between "*this" and "rhs" */ }
};

अब == तुलना 2 ऐसी वस्तुओं के लिए कानूनी है।


11

C ++ 20 में शुरू करना , एक डिफ़ॉल्ट तीन तरह के तुलना ऑपरेटर ("स्पेसशिप" ऑपरेटर) की घोषणा करके एक वर्ग के लिए डिफ़ॉल्ट तुलना ऑपरेटरों ( ==, <=आदि) का एक पूरा सेट जोड़ना संभव होना चाहिए :

struct Point {
    int x;
    int y;
    auto operator<=>(const Point&) const = default;
};

एक अनुरूप C ++ 20 संकलक के साथ, MyStruct1 और MyStruct2 के लिए उस पंक्ति को जोड़ने से समानता तुलना की अनुमति देने के लिए पर्याप्त हो सकता है, MyStruct2 की परिभाषा को संगत मानते हुए।


2

तुलना C या C ++ में स्ट्रक्चर्स पर काम नहीं करती है। इसके बजाय खेतों से तुलना करें।


2

डिफ़ॉल्ट रूप से संरचना में एक ==ऑपरेटर नहीं होता है । आपको अपना स्वयं का कार्यान्वयन लिखना होगा:

bool MyStruct1::operator==(const MyStruct1 &other) const {
    ...  // Compare the values, and return a bool result.
  }

0

बॉक्स से, == ऑपरेटर केवल प्राइमेटीज़ के लिए काम करता है। अपना कोड काम करने के लिए, आपको अपनी संरचना के लिए == ऑपरेटर को ओवरलोड करना होगा।


0

क्योंकि आपने अपनी संरचना के लिए तुलना ऑपरेटर नहीं लिखा था। संकलक आपके लिए इसे उत्पन्न नहीं करता है, इसलिए यदि आप तुलना करना चाहते हैं, तो आपको इसे स्वयं लिखना होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.