समानता संचालक के साथ मौजूदा कोड को तोड़ने वाला C ++ 20 व्यवहार?

Jan 10 2021

मैं इस सवाल पर बहस करते हुए भाग गया ।

मैंने इसे बूस्ट ऑपरेटर्स का उपयोग करते हुए सभी तरह से ट्रिम कर दिया :

  1. कंपाइलर एक्सप्लोरर C ++ 17 C ++ 20

    #include <boost/operators.hpp>
    
    struct F : boost::totally_ordered1<F, boost::totally_ordered2<F, int>> {
        /*implicit*/ F(int t_) : t(t_) {}
        bool operator==(F const& o) const { return t == o.t; }
        bool operator< (F const& o) const { return t <  o.t; }
      private: int t;
    };
    
    int main() {
        #pragma GCC diagnostic ignored "-Wunused"
        F { 42 } == F{ 42 }; // OKAY
        42 == F{42};         // C++17 OK, C++20 infinite recursion
        F { 42 } == 42;      // C++17 OK, C++20 infinite recursion
    }
    

    यह कार्यक्रम जीसीसी और क्लैंग दोनों में सी ++ 17 (इब्सन / आसन सक्षम) के साथ संकलित करता है और ठीक चलता है।

  2. जब आप अंतर्निहित निर्माता को बदलते हैं explicit, तो समस्याग्रस्त लाइनें स्पष्ट रूप से C ++ 17 पर संकलित नहीं होती हैं

आश्चर्यजनक रूप से दोनों संस्करण C ++ 20 ( v1 और v2 ) पर संकलित हैं , लेकिन वे C ++ 17 पर संकलित नहीं होने वाली दो पंक्तियों पर अनंत पुनरावृत्ति (दुर्घटना स्तर पर निर्भरता या तंग लूप ) का नेतृत्व करते हैं ।

जाहिर है C ++ 20 में अपग्रेड करके इस तरह के साइलेंट बग रेंगना चिंताजनक है।

प्रशन:

  • क्या यह सी ++ 20 व्यवहार अनुरूप है (मुझे इसकी उम्मीद है)
  • क्या वास्तव में हस्तक्षेप है? मुझे संदेह है कि यह c ++ 20 के नए "स्पेसशिप ऑपरेटर" समर्थन के कारण हो सकता है, लेकिन यह नहीं समझता कि यह इस कोड के व्यवहार को कैसे बदलता है।

जवाब

81 Barry Jan 10 2021 at 07:27

दरअसल, C ++ 20 दुर्भाग्य से इस कोड को असीम रूप से पुनरावर्ती बनाता है।

यहाँ एक कम उदाहरण है:

struct F {
    /*implicit*/ F(int t_) : t(t_) {}

    // member: #1
    bool operator==(F const& o) const { return t == o.t; }

    // non-member: #2
    friend bool operator==(const int& y, const F& x) { return x == y; }

private:
    int t;
};

आइए जरा देखें 42 == F{42}

C ++ 17 में, हमारे पास केवल एक उम्मीदवार था: गैर-सदस्य उम्मीदवार ( #2), इसलिए हम उसका चयन करते हैं। इसका निकाय, अपने x == yआप में केवल एक उम्मीदवार है: सदस्य उम्मीदवार ( #1) जिसमें निहित रूप yसे एक में परिवर्तित करना शामिल है F। और फिर वह सदस्य उम्मीदवार दो पूर्णांक सदस्यों की तुलना करता है और यह पूरी तरह से ठीक है।

C ++ 20 में, प्रारंभिक अभिव्यक्ति में 42 == F{42}अब दो उम्मीदवार हैं: दोनों गैर-सदस्य उम्मीदवार ( #2) पहले की तरह और अब उलटे सदस्य उम्मीदवार ( #1उलट) भी। #2बेहतर मैच है - हम किसी रूपांतरण को लागू करने के बजाय दोनों तर्कों से मेल खाते हैं, इसलिए इसे चुना गया है।

अब, हालांकि, x == yअब दो उम्मीदवार हैं: सदस्य उम्मीदवार फिर से ( #1), लेकिन उलटा गैर-सदस्य उम्मीदवार ( #2उलट) भी। #2एक ही कारण के लिए फिर से बेहतर मैच है कि पहले एक बेहतर मैच था: कोई रूपांतरण आवश्यक नहीं। इसलिए हम y == xइसके बजाय मूल्यांकन करते हैं। अनंत पुनरावृत्ति।

गैर-उलट उम्मीदवारों को उलटे उम्मीदवारों के लिए पसंद किया जाता है, लेकिन केवल एक टाईब्रेकर के रूप में। बेहतर रूपांतरण क्रम हमेशा पहले होता है।


ठीक है महान, हम इसे कैसे ठीक कर सकते हैं? सबसे सरल विकल्प गैर-सदस्य उम्मीदवार को पूरी तरह से हटा रहा है:

struct F {
    /*implicit*/ F(int t_) : t(t_) {}

    bool operator==(F const& o) const { return t == o.t; }

private:
    int t;
};

42 == F{42}यहाँ के रूप में मूल्यांकन करता है F{42}.operator==(42), जो ठीक काम करता है।

यदि हम गैर-सदस्य उम्मीदवार रखना चाहते हैं, तो हम अपने प्रत्यावर्तित उम्मीदवार को स्पष्ट रूप से जोड़ सकते हैं:

struct F {
    /*implicit*/ F(int t_) : t(t_) {}
    bool operator==(F const& o) const { return t == o.t; }
    bool operator==(int i) const { return t == i; }
    friend bool operator==(const int& y, const F& x) { return x == y; }

private:
    int t;
};

यह 42 == F{42}अभी भी गैर-सदस्य उम्मीदवार का चयन करता है, लेकिन अब x == yनिकाय में सदस्य उम्मीदवार को पसंद करेंगे, जो तब सामान्य समानता करता है।

यह अंतिम संस्करण गैर-सदस्य उम्मीदवार को भी हटा सकता है। निम्नलिखित सभी परीक्षण मामलों के लिए पुनरावृत्ति के बिना भी काम करता है (और यह है कि मैं C ++ 20 की तुलना में आगे कैसे लिखूंगा):

struct F {
    /*implicit*/ F(int t_) : t(t_) {}
    bool operator==(F const& o) const { return t == o.t; }
    bool operator==(int i) const { return t == i; }

private:
    int t;
};