c++ कॉन्स्ट से गैर-कॉन्स्ट की अनुमति क्यों है?



c++11 templates (1)

क्यों C ++ निम्नलिखित कोड को संकलित करने की अनुमति देता है?

std::unordered_map<std::string, int> m;
// ...
for (const std::pair<std::string, int>& p: m)
{
    // ...
}

स्कॉट मेयेर्स के प्रभावी आधुनिक सी ++ (पृष्ठ 40-41) के अनुसार:

[...] एक std::unordered_map का मुख्य भाग const , इसलिए हैश तालिका में std::pair का प्रकार (जो कि एक std::unordered_map है) std::pair<std::string, int> , यह std::pair <const std::string, int> । लेकिन यह ऊपर वाली लूप में चर p लिए घोषित प्रकार नहीं है। परिणामस्वरूप, कंपलर std::pair<const std::string, int> ऑब्जेक्ट्स के लिए std::pair<const std::string, int> ऑब्जेक्ट्स (यानी, हैश तालिका में क्या है) को परिवर्तित करने का एक तरीका खोजने का प्रयास करेंगे। ( p लिए घोषित प्रकार) वे उस प्रकार के एक अस्थायी ऑब्जेक्ट बनाकर सफल होंगे, जो प्रत्येक ऑब्जेक्ट को m में कॉपी करके बाइंड करना चाहते हैं, फिर उस अस्थायी ऑब्जेक्ट पर संदर्भ पृष्ठ बाध्य करते हैं। प्रत्येक लूप पुनरावृत्ति के अंत में, अस्थायी ऑब्जेक्ट को नष्ट कर दिया जाएगा। यदि आपने यह लूप लिखा है, तो आप इस व्यवहार से आश्चर्यचकित होंगे, क्योंकि आप लगभग निश्चित रूप से केवल प्रत्येक तत्व में संदर्भ p को बाध्य करने का इरादा रखते हैं।

इस अंतर्निहित रूपांतरण की अनुमति का क्या लाभ है? क्या कुछ सामान्य उपयोग के मामले हैं, जहां डेवलपर इस अंतर्निहित रूपांतरण की अपेक्षा करेगा / (एक कंपाइलर त्रुटि प्राप्त करने के बजाय)?

https://code.i-harness.com


एक मानकों के अनुरूप कंपाइलर निम्नानुसार पाश के लिए "देख" करेगा:

auto&& __range = m; 
for (auto __begin = std::begin(m), __end = std::end(m); __begin != __end; ++__begin) { 
    const std::pair<std::string, int>& p = *__begin;
    //loop_statement 
}

मूलभूत रूप से आपके सवाल का कारण यह है कि निम्नलिखित कोड की अनुमति क्यों है:

std::pair<std::string, int> p = std::pair<const std::string, int>{};

ध्यान दें कि मैंने const& p हिस्सा छोड़ा, क्योंकि यह प्रासंगिक नहीं है रूपांतरण एक समान है, केवल अंतर यह है कि अस्थायी प्रतिलिपि के बजाय संदर्भ के लिए बाध्य है।

यदि आप सोच रहे हैं कि ओपी के स्निपेट एक गैर-कॉन्स्ट्रेंट संदर्भ के साथ काम क्यों नहीं करता है, तो रूपांतरण यही वजह है कि रूपांतरण का नतीजा एक अस्थायी ऑब्जेक्ट है, और क्योंकि अस्थायी किसी भी परिवर्तन बेकार हो जाएगा (इसका जीवनकाल बढ़ाया नहीं गया है और इसलिए इसे तुरंत नष्ट कर दिया गया है), इसलिए भाषा इसे अस्वीकार करती है

इसकी अनुमति है क्योंकि std::pair में एक कन्स्ट्रक्टर है जो इस रूपांतरण को सक्षम करता है।

template< class U1, class U2 >
pair( const pair<U1, U2>& p );

आपके मामले में, U1 को const std::string और U2 रूप में int रूप में अनुमानित किया जाता है। यह वास्तव में कोई फर्क नहीं पड़ता कि सीवी क्वालिफायर U1 और U2 क्या है, क्योंकि p तत्वों को कॉपी किया जाता है

इसका लाभ यही है कि इसके लिए क्यों अनुमति दी जाती है:

const int zero{};
int zero2 = zero;

उदाहरण के लिए, निम्न गैर-यथार्थवादी उदाहरण पर विचार करें:

struct {
    std::pair<int, int> pos;
} player;

std::pair<const int, const int> treasure{1, 2}; // position of treasure
player.pos = treasure; // ok

अब क्या होगा, जैसा कि आप कहते हैं, यह रूपांतरण किसी कारण के लिए नहीं था प्रोग्रामर को क्या करना होगा?

player.pos.first = treasure.first;
player.pos.second = treasure.second;

यदि यह भी अनुमति नहीं दी जाती है, तो ऊपर दिए गए शून्य के साथ मामला भी अनुमति नहीं दी जाएगी, जो वास्तव में समझ में नहीं आता है, क्योंकि आप zero नकल कर रहे हैं, इसलिए आप इसे संशोधित कर सकते हैं या नहीं, इससे कोई फर्क नहीं पड़ेगा क्योंकि एक पूरी तरह से अलग आपरेशन है

यदि यह अनुमति दी जाती है, तो player.pos = treasure; क्यों होगा player.pos = treasure; अस्वीकार, क्योंकि केवल एक चीज है जो इसे कॉपी करता है? ऊपर की तरह, यह कोई फर्क नहीं पड़ता कि आप treasure के तत्वों को बदल सकते हैं, क्योंकि आप केवल उन्हें नकल कर रहे हैं।

यही कारण है कि आपको const auto& इस्तेमाल करना चाहिए const auto& रेंज वाले लूप के लिए (और यहां तक ​​कि सामान्य तौर पर?) का उपयोग करना चाहिए क्योंकि यह आपकी प्रतिलिपि से बच सकता है अगर आप सावधान न हों





const