कैसे एक गैर-कास्ट संदर्भ एक अस्थायी वस्तु के लिए बाध्य नहीं कर सकता है?


226

किसी अस्थायी ऑब्जेक्ट के लिए गैर-कॉन्स्टेंस संदर्भ प्राप्त करने की अनुमति क्यों नहीं है, जो फ़ंक्शन getx()देता है? स्पष्ट रूप से, यह सी ++ मानक द्वारा निषिद्ध है, लेकिन मुझे इस तरह के प्रतिबंध के उद्देश्य में दिलचस्पी है, मानक के संदर्भ में नहीं

struct X
{
    X& ref() { return *this; }
};

X getx() { return X();}

void g(X & x) {}    

int f()
{
    const X& x = getx(); // OK
    X& x = getx(); // error
    X& x = getx().ref(); // OK
    g(getx()); //error
    g(getx().ref()); //OK
    return 0;
}
  1. यह स्पष्ट है कि वस्तु का जीवनकाल कारण नहीं हो सकता है, क्योंकि C ++ मानक द्वारा किसी वस्तु का निरंतर संदर्भ निषिद्ध नहीं है
  2. यह स्पष्ट है कि अस्थायी वस्तु ऊपर के नमूने में स्थिर नहीं है, क्योंकि गैर-स्थिर कार्यों के लिए कॉल की अनुमति है। उदाहरण के लिए, ref()अस्थायी वस्तु को संशोधित कर सकता है।
  3. इसके अलावा, ref()आप संकलक को मूर्ख बनाने और इस अस्थायी ऑब्जेक्ट का लिंक प्राप्त करने की अनुमति देता है और जो हमारी समस्या को हल करता है।

के अतिरिक्त:

वे कहते हैं "कॉन्स्ट रेफरेंस को एक अस्थायी ऑब्जेक्ट असाइन करना इस ऑब्जेक्ट के जीवनकाल का विस्तार करता है" और "नॉन-कॉस्ट रेफरेंस के बारे में कुछ नहीं कहा जाता है"। मेरा अतिरिक्त प्रश्न । निम्नलिखित असाइनमेंट अस्थायी वस्तु के जीवनकाल का विस्तार करता है?

X& x = getx().ref(); // OK

4
मैं "वस्तु के जीवनकाल का कारण नहीं हो सकता है" से असहमत हूं, सिर्फ इसलिए कि यह मानक में कहा गया है, कि कॉन्स्टेक्शन के संदर्भ में एक अस्थायी ऑब्जेक्ट को असाइन करना इस ऑब्जेक्ट के जीवनकाल को कॉन्स्टेंस रेफरेंस के जीवनकाल तक बढ़ाता है। हालांकि नॉन-
कॉस्ट

3
खैर, क्या कारण है कि "कुछ भी नहीं के बारे में कहा जाता है गैर-संदर्भ हालांकि ..."। यह मेरे सवाल का एक हिस्सा है। क्या इसमें कोई दम है? हो सकता है कि मानक के लेखक नॉन-कास्ट संदर्भों के बारे में भूल गए हों और जल्द ही हम अगला कोर इश्यू देखेंगे।
बजे एलेक्सी मालिस्टोव

2
गॉट # 88: "सबसे महत्वपूर्ण कास्ट" के लिए एक उम्मीदवार। जड़ी
फर्नांडो नीटो

4
@ मिचेल: वीसी गैर-कॉन्स्टेबल संदर्भों के लिए प्रतिद्वंद्वियों को बांधता है। वे इसे एक विशेषता कहते हैं, लेकिन वास्तव में यह एक बग है। (ध्यान दें कि यह एक बग नहीं है क्योंकि यह स्वाभाविक रूप से अतार्किक है, लेकिन क्योंकि यह मूर्खतापूर्ण त्रुटियों को रोकने के लिए स्पष्ट रूप से खारिज किया गया था।)
sbi

जवाबों:


97

इस विजुअल C ++ ब्लॉग के लेख से संदर्भों के संदर्भ में :

... सी ++ आपको गलती से अस्थायी रूप से संशोधित नहीं करना चाहता है, लेकिन एक गैर-कांस्टेबल सदस्य फ़ंक्शन को सीधे रूप से संशोधित करने योग्य फ़ंक्शन पर कॉल करना स्पष्ट है, इसलिए इसकी अनुमति है ...

मूल रूप से, आपको अस्थायी कारणों को संशोधित करने का प्रयास नहीं करना चाहिए क्योंकि वे अस्थायी वस्तुएं हैं और अब किसी भी क्षण मर जाएंगे। जिस कारण से आपको नॉन-कास्ट विधियों को कॉल करने की अनुमति दी गई है, ठीक है, जब तक आप जानते हैं कि आप क्या कर रहे हैं और आप इसके बारे में स्पष्ट हैं (जैसे, reinterpret_cast का उपयोग करके) कुछ "बेवकूफ" चीजें करने के लिए आपका स्वागत है। लेकिन अगर आप एक अस्थायी को गैर-कॉन्स्टेबल संदर्भ में बाँधते हैं, तो आप इसे "हमेशा के लिए" पास रख सकते हैं, ताकि आपके ऑब्जेक्ट का हेरफेर गायब हो जाए, क्योंकि कहीं न कहीं जिस तरह से आप पूरी तरह से भूल गए थे यह एक अस्थायी था।

अगर मैं तुम थे, मैं अपने कार्यों के डिजाइन पर फिर से विचार करूंगा। क्यों जी () संदर्भ को स्वीकार कर रहा है, क्या यह पैरामीटर को संशोधित करता है? यदि नहीं, तो इसे संदर्भ दें, यदि हाँ, तो आप इसे अस्थायी रूप से पारित करने का प्रयास क्यों करते हैं, क्या आपको इसकी परवाह नहीं है कि यह एक अस्थायी है जिसे आप संशोधित कर रहे हैं? गेटेक्स () वैसे भी अस्थायी क्यों लौट रहा है? यदि आप हमारे साथ अपने वास्तविक परिदृश्य को साझा करते हैं और जो आप पूरा करने की कोशिश कर रहे हैं, तो आपको यह करने के लिए कुछ अच्छे सुझाव मिल सकते हैं।

भाषा के खिलाफ जाना और संकलक को बेवकूफ बनाना शायद ही कभी समस्याओं को हल करता है - आमतौर पर यह समस्याएं पैदा करता है।


संपादित करें: टिप्पणी में प्रश्नों को संबोधित करना: 1) X& x = getx().ref(); // OK when will x die?- मुझे नहीं पता है और मुझे परवाह नहीं है, क्योंकि यह वास्तव में "भाषा के खिलाफ जाने" से मेरा मतलब है। भाषा कहती है, "जब तक कि वे संदर्भ के दायरे से बाहर नहीं जाते हैं, तब तक वे अस्थायी बयान के अंत में मर जाते हैं, जब तक कि वे संदर्भ के लिए बाध्य नहीं होते हैं।" उस नियम को लागू करते हुए, ऐसा लगता है कि x अगले बयान की शुरुआत में पहले से ही मर चुका है, क्योंकि यह कॉन्स्ट रेफरेंस के लिए बाध्य नहीं है (कंपाइलर को पता नहीं है कि रेफ () रिटर्न क्या है)। हालांकि यह सिर्फ एक अनुमान है।

2) मैंने उद्देश्य स्पष्ट रूप से कहा: आपको अस्थायी लोगों को संशोधित करने की अनुमति नहीं है, क्योंकि यह सिर्फ समझ में नहीं आता है (सी ++ 0x rvalue संदर्भों की अनदेखी)। सवाल "फिर मुझे गैर-कास्ट सदस्यों को कॉल करने की अनुमति क्यों दी गई है?" एक अच्छा है, लेकिन मेरे पास पहले से ही ऊपर बताए गए से बेहतर जवाब नहीं है।

3) ठीक है, अगर मैं X& x = getx().ref();बयान के अंत में मरने के बारे में x सही हूं , तो समस्याएं स्पष्ट हैं।

वैसे भी, आपके प्रश्न और टिप्पणियों के आधार पर मुझे नहीं लगता कि ये अतिरिक्त उत्तर आपको संतुष्ट करेंगे। यहाँ एक अंतिम प्रयास / सारांश दिया गया है: C ++ समिति ने निर्णय लिया कि यह अस्थायी लोगों को संशोधित करने का कोई मतलब नहीं है, इसलिए, उन्होंने गैर-कास्ट संदर्भों के लिए बाध्यकारी को अस्वीकार कर दिया। हो सकता है कुछ संकलक कार्यान्वयन या ऐतिहासिक मुद्दे भी शामिल थे, मुझे नहीं पता। फिर, कुछ विशिष्ट मामले सामने आए, और यह तय किया गया कि सभी बाधाओं के खिलाफ, वे अभी भी गैर-कॉन्स्टेंट विधि के माध्यम से प्रत्यक्ष संशोधन की अनुमति देंगे। लेकिन यह एक अपवाद है - आपको आमतौर पर अस्थायी लोगों को संशोधित करने की अनुमति नहीं है। हां, C ++ अक्सर अजीब होता है।


2
@ एसबीके: 1) वास्तव में, उचित वाक्यांश है: "... पूर्ण अभिव्यक्ति के अंत में ..."। एक "पूर्ण अभिव्यक्ति", मेरा मानना ​​है कि इसे किसी अन्य अभिव्यक्ति की उप-अभिव्यक्ति के रूप में परिभाषित नहीं किया गया है। क्या यह हमेशा "बयान के अंत" के समान है, मुझे यकीन नहीं है।
sbi

5
@sbk: 2) असल में, आप कर रहे हैं rvalues (temporaries) को संशोधित करने की अनुमति दी। यह अंतर्निहित प्रकारों ( intआदि) के लिए निषिद्ध है , लेकिन उपयोगकर्ता-परिभाषित प्रकारों के लिए इसकी अनुमति है (std::string("A")+"B").append("C"):।
sbi

6
@sbk: 3) स्ट्रॉन्स्ट्रुप (डी एंड ई में) गैर-कॉन्स्टेबल संदर्भों के लिए प्रतिद्वंद्वियों के बंधन को अस्वीकार करने के लिए कारण देता है, अगर एलेक्सी g()ऑब्जेक्ट को संशोधित करेगा (जो आप गैर-कॉस्ट संदर्भ लेने वाले फ़ंक्शन से उम्मीद करेंगे), यह एक ऐसी वस्तु को संशोधित करेगा जो मरने वाली है, इसलिए किसी को भी संशोधित मूल्य पर नहीं मिल सकता है। वह कहते हैं कि यह, सबसे अधिक संभावना है, एक त्रुटि है।
sbi

2
@ एसबीके: मुझे खेद है अगर मैंने आपको नाराज किया है, लेकिन मुझे नहीं लगता कि 2) नाइटपिटिंग है। जब तक आप उन्हें ऐसा नहीं करते हैं, तब तक रिवेल्यूशन कांस्ट नहीं होता है और जब तक वे बिल्ट-इन नहीं होते हैं, तब तक आप उन्हें बदल सकते हैं। मुझे यह समझने में थोड़ा समय लगा कि क्या, उदाहरण के लिए, स्ट्रिंग उदाहरण, मैं कुछ गलत करता हूं (JFTR: I don’t), इसलिए मैं इस अंतर को गंभीर रूप से लेता हूं।
sbi

5
मैं sbi से सहमत हूँ - यह मामला बिल्कुल भी नहीं है । यह सभी प्रकार के चाल शब्दार्थों का आधार है जो वर्ग प्रकार के प्रतिद्वंद्वियों को गैर-कब्जे में रखा जाता है।
जोहान्स शाउब -

38

आपके कोड में getx()एक अस्थायी वस्तु वापस आती है, एक तथाकथित "प्रतिद्वंद्विता"। आप वस्तुओं (aka। चर) में rvalues ​​को कॉपी कर सकते हैं या उन्हें कॉन्स्ट सन्दर्भ में बाँध सकते हैं (जो संदर्भ के जीवन के अंत तक उनके जीवन-काल का विस्तार करेगा)। आप गैर-कॉन्स्टेंस संदर्भों के लिए प्रतिद्वंद्वियों को बांध नहीं सकते।

यह एक जानबूझकर डिजाइन निर्णय था ताकि उपयोगकर्ताओं को गलती से किसी वस्तु को संशोधित करने से रोका जा सके जो अभिव्यक्ति के अंत में मरने जा रहा है:

g(getx()); // g() would modify an object without anyone being able to observe

यदि आप ऐसा करना चाहते हैं, तो आपको या तो स्थानीय प्रति या वस्तु को पहले बनाना होगा या उसे एक संदर्भ में बाँधना होगा:

X x1 = getx();
const X& x2 = getx(); // extend lifetime of temporary to lifetime of const reference

g(x1); // fine
g(x2); // can't bind a const reference to a non-const reference

ध्यान दें कि अगले C ++ मानक में रेवाल्यू संदर्भ शामिल होंगे। जिसे आप संदर्भ के रूप में जानते हैं, इसलिए इसे "लवल्यू रेफरेंस" कहा जा रहा है। आपको संदर्भों को फिर से बनाने के लिए प्रतिद्वंद्वियों को बांधने की अनुमति दी जाएगी और आप "rvalue-ness" पर कार्यों को अधिभारित कर सकते हैं:

void g(X&);   // #1, takes an ordinary (lvalue) reference
void g(X&&);  // #2, takes an rvalue reference

X x; 
g(x);      // calls #1
g(getx()); // calls #2
g(X());    // calls #2, too

संदर्भ संदर्भों के पीछे का विचार यह है कि चूंकि ये वस्तुएं वैसे भी मरने वाली हैं, इसलिए आप उस ज्ञान का लाभ उठा सकते हैं और इसे लागू कर सकते हैं जिसे "मूवमेंट शब्दार्थ" कहा जाता है, एक निश्चित प्रकार का अनुकूलन:

class X {
  X(X&& rhs)
    : pimpl( rhs.pimpl ) // steal rhs' data...
  {
    rhs.pimpl = NULL; // ...and leave it empty, but deconstructible
  }

  data* pimpl; // you would use a smart ptr, of course
};


X x(getx()); // x will steal the rvalue's data, leaving the temporary object empty

2
नमस्ते, यह एक भयानक जवाब है। एक बात जानने की जरूरत है, g(getx())क्योंकि यह काम नहीं करता है क्योंकि इसका हस्ताक्षर है g(X& x)और get(x)एक अस्थायी वस्तु लौटाता है, इसलिए हम एक अस्थायी वस्तु ( प्रतिद्वंद्विता ) को एक गैर-निरंतर संदर्भ में नहीं बांध सकते , सही है? और आपके पहले कोड के टुकड़े में, मुझे लगता है कि यह const X& x2 = getx();इसके बजाय होगा const X& x1 = getx();..
सेक्सीबीटी

1
यह लिखने के 5 साल बाद मेरे जवाब में इस बग को इंगित करने के लिए धन्यवाद! :-/हां, आपका तर्क सही है, हालांकि थोड़ा पीछे की ओर: हम अस्थायी const( गैर- लंबित) संदर्भों को नहीं बांध सकते हैं , और इसलिए अस्थायी getx()(और नहीं get(x)) द्वारा लौटाए गए तर्क के संदर्भ में बाध्य नहीं हो सकते g()
sbi

उम्म, आपका क्या मतलब था getx()(और नहीं get(x)) ?
सेक्सीबेस्ट

जब मैं "... getx () (और नहीं मिल (x)) ..." लिखता हूं, तो मेरा मतलब है कि फ़ंक्शन का नाम है getx(), और नहीं get(x)(जैसा आपने लिखा)।
sbi

यह उत्तर शब्दावली को मिश्रित करता है। एक प्रतिद्वंद्विता एक अभिव्यक्ति श्रेणी है। एक अस्थायी वस्तु एक वस्तु है। एक प्रतिद्वंद्विता एक अस्थायी वस्तु को निरूपित कर सकती है या नहीं कर सकती है; और एक अस्थायी वस्तु को एक प्रतिद्वंद्विता द्वारा चिह्नित या नहीं किया जा सकता है।
MM

16

आप जो दिखा रहे हैं, वह यह है कि ऑपरेटर की चेनिंग की अनुमति है।

 X& x = getx().ref(); // OK

अभिव्यक्ति 'getx ()। Ref। (); और इसे 'x' को असाइनमेंट से पहले पूरा किया जाता है।

ध्यान दें कि getx () एक संदर्भ नहीं देता है लेकिन स्थानीय संदर्भ में पूरी तरह से बनाई गई वस्तु है। ऑब्जेक्ट अस्थायी है, लेकिन यह कॉन्स्टेबल नहीं है , इस प्रकार आप अन्य तरीकों से कॉल कर सकते हैं जिससे वैल्यू की गणना हो सकती है या अन्य साइड इफेक्ट हो सकते हैं।

// It would allow things like this.
getPipeline().procInstr(1).procInstr(2).procInstr(3);

// or more commonly
std::cout << getManiplator() << 5;

इसके बेहतर उदाहरण के लिए इस उत्तर के अंत को देखें

आप एक अस्थायी संदर्भ के लिए बाध्य नहीं कर सकते क्योंकि ऐसा करने से किसी ऑब्जेक्ट का एक संदर्भ उत्पन्न होगा जो अभिव्यक्ति के अंत में नष्ट हो जाएगा और इस प्रकार आपको एक झूलने वाले संदर्भ के साथ छोड़ देगा (जो कि अयोग्य है और मानक को अकारण पसंद नहीं है)।

रेफरी द्वारा लौटाया गया मूल्य () एक वैध संदर्भ है लेकिन विधि उस वस्तु के जीवनकाल पर कोई ध्यान नहीं देती है जो वह लौट रही है (क्योंकि उसके संदर्भ में यह जानकारी नहीं हो सकती है)। आपने मूल रूप से इसके बराबर काम किया है:

x& = const_cast<x&>(getX());

एक अस्थायी ऑब्जेक्ट के एक कॉस्ट संदर्भ के साथ ऐसा करने के लिए कारण ठीक है कि मानक संदर्भ के जीवन काल के लिए अस्थायी के जीवन काल का विस्तार करता है इसलिए अस्थायी वस्तुओं के जीवन काल को बयान के अंत से आगे बढ़ाया जाता है।

तो केवल शेष प्रश्न यह है कि मानक, वस्तु के जीवन को बयान के अंत से परे बढ़ाने के लिए अस्थायी लोगों के संदर्भ को अनुमति क्यों नहीं देना चाहता है?

मेरा मानना ​​है कि ऐसा इसलिए है क्योंकि ऐसा करने से संकलक को अस्थायी वस्तुओं के लिए सही होने में बहुत मुश्किल होगी। यह अस्थायी उपयोगों के लिए संदर्भ के लिए किया गया था क्योंकि इसका सीमित उपयोग है और इस प्रकार आपको कुछ उपयोगी करने के लिए ऑब्जेक्ट की प्रतिलिपि बनाने के लिए मजबूर किया गया है लेकिन कुछ सीमित कार्यक्षमता प्रदान करता है।

इस स्थिति के बारे में सोचो:

int getI() { return 5;}
int x& = getI();

x++; // Note x is an alias to a variable. What variable are you updating.

इस अस्थायी वस्तु के जीवनकाल का विस्तार बहुत भ्रमित करने वाला है।
जबकि निम्नलिखित:

int const& y = getI();

आपको कोड देगा कि यह उपयोग करने और समझने के लिए सहज है।

यदि आप मूल्य को संशोधित करना चाहते हैं तो आपको एक चर पर मूल्य वापस करना चाहिए। यदि आप कार्य को वापस करने की लागत को फ़ंक्शन से वापस लेने से बचने की कोशिश कर रहे हैं (जैसा कि ऐसा लगता है कि ऑब्जेक्ट वापस कॉपी किया गया है (तकनीकी रूप से यह है)। फिर कंपाइलर को परेशान न करें 'रिटर्न वैल्यू ऑप्टिमाइजेशन' में बहुत अच्छा है


3
"तो केवल शेष प्रश्न यह है कि मानक, वस्तु के जीवन को बयान के अंत से परे बढ़ाने के लिए अस्थायी लोगों के संदर्भ को अनुमति क्यों नहीं देना चाहता है?" बस इतना ही! आप मेरा प्रश्न समझें । लेकिन मैं आपकी राय से असहमत हूं। आप कहते हैं कि "कंपाइलर को बहुत कठिन बनाएं" लेकिन यह कॉन्स्ट रेफरेंस के लिए किया गया था। आप अपने नमूने में कहते हैं "नोट x एक चर के लिए एक अन्य नाम है। आप किस चर को अद्यतन कर रहे हैं।" कोई दिक्कत नहीं है। अद्वितीय चर (अस्थायी) है। कुछ अस्थायी ऑब्जेक्ट (5 के बराबर) को बदलना होगा।
एलेक्सी मालिस्टोव

@ मॉर्टिन: डेंग्लिंग संदर्भ केवल अयोग्य नहीं हैं। विधि में बाद में पहुंचने पर वे गंभीर कीड़े पैदा कर सकते हैं!
mmmmmmmm

1
@Alexey: ध्यान दें कि तथ्य यह है कि इसे एक कॉस्ट रेफरेंस के लिए बाध्य करना एक अस्थायी जीवनकाल को बढ़ाता है एक अपवाद है जिसे मैन्युअल रूप से जोड़ने के लिए जानबूझकर जोड़ा गया है (TTBOMK)। नॉन-कॉस्ट संदर्भों के लिए कोई अपवाद नहीं जोड़ा गया था, क्योंकि एक गैर-कॉस्ट-रेफरेंस को अस्थायी रूप से बाइंड करना एक प्रोग्रामर त्रुटि होने की सबसे अधिक संभावना थी।
sbi

1
@alexy: एक अदृश्य चर का संदर्भ! नहीं कि सहज।
मार्टिन यॉर्क

const_cast<x&>(getX());मतलब नहीं है
जिज्ञासु

10

C ++ FAQ ( बोल्ड फ़ेसिंग मेरा)में क्यों चर्चा की गई है:

C ++ में, नॉन-कास्ट संदर्भ lvalues ​​से बंध सकते हैं और const संदर्भ lvalues ​​या rvalues ​​से बाँध सकते हैं, लेकिन ऐसा कुछ भी नहीं है जो किसी non-const rvalue से बाँध सके। यही कारण है कि लोगों को उनके नए मूल्य का उपयोग करने से पहले नष्ट होने वाले अस्थायी लोगों के मूल्यों को बदलने से बचाने के लिए है । उदाहरण के लिए:

void incr(int& a) { ++a; }
int i = 0;
incr(i);    // i becomes 1
incr(0);    // error: 0 is not an lvalue

अगर उस incr (0) को या तो कुछ अस्थायी की अनुमति दी गई थी जिसे कभी किसी ने नहीं देखा होगा तो वह बढ़ जाएगा या - इससे भी बदतर - 0 का मान बन जाएगा। बाद वाला मूर्खतापूर्ण लगता है, लेकिन वास्तव में एक ऐसा बग था जो शुरुआती फोरट्रान कंपाइलरों में सेट होता है मान 0 को रखने के लिए मेमोरी लोकेशन अलग रखें।


उस फोरट्रान "जीरो बग" द्वारा काटे गए प्रोग्रामर का चेहरा देखने के लिए मज़ेदार होगा! x * 0 देता है x? क्या? क्या??
जॉन डी

वह अंतिम तर्क विशेष रूप से कमजोर है। उल्लेख के लायक कोई भी कंपाइलर वास्तव में कभी भी 0 से 1 के मूल्य को नहीं बदलेगा, या incr(0);इस तरह से भी व्याख्या करेगा । जाहिर है, अगर यह अनुमति दी गई थी, तो इसे एक अस्थायी पूर्णांक बनाने और इसे पास करने के रूप में समझा जाएगाincr()
user3204459

6

मुख्य मुद्दा यह है कि

g(getx()); //error

एक तार्किक त्रुटि है: gपरिणाम को संशोधित कर रहा है getx()लेकिन आपके पास संशोधित वस्तु की जांच करने का कोई मौका नहीं है। यदि gइसके पैरामीटर को संशोधित करने की आवश्यकता नहीं थी, तो इसे एक लैवल्यू संदर्भ की आवश्यकता नहीं होगी, यह पैरामीटर को मान या कॉन्स्टेंस संदर्भ द्वारा ले सकता था।

const X& x = getx(); // OK

मान्य है क्योंकि आपको कभी-कभी एक अभिव्यक्ति के परिणाम का पुन: उपयोग करने की आवश्यकता होती है, और यह बहुत स्पष्ट है कि आप एक अस्थायी वस्तु के साथ काम कर रहे हैं।

हालांकि इसे बनाना संभव नहीं है

X& x = getx(); // error

वैध बनाने के बिना g(getx())वैध है, जो कि भाषा डिजाइनर पहली जगह में बचने की कोशिश कर रहे थे।

g(getx().ref()); //OK

मान्य है क्योंकि विधियाँ केवल कॉन्स्टेंट-नेस के बारे में ही जानती हैं this, वे नहीं जानती हैं कि उन्हें एक अंतराल पर या एक प्रतिद्वंद्विता पर बुलाया जाता है या नहीं।

C ++ में हमेशा की तरह, इस नियम के लिए आपके पास वर्कअराउंड है, लेकिन आपको कंपाइलर को संकेत देना होगा कि आप जानते हैं कि आप क्या कर रहे हैं:

g(const_cast<x&>(getX()));

5

मूल प्रश्न की तरह लगता है कि यह अनुमति क्यों नहीं दी गई है इसका स्पष्ट रूप से उत्तर दिया गया है: "क्योंकि यह सबसे अधिक संभावना है एक त्रुटि"।

FWIW, मुझे लगा कि मैं दिखाऊंगा कि यह कैसे किया जा सकता है, हालांकि मुझे नहीं लगता कि यह एक अच्छी तकनीक है।

जिस कारण से मैं कभी-कभी एक गैर-कॉन्स्टेबल संदर्भ लेने वाली विधि के लिए एक अस्थायी पास करना चाहता हूं, वह जानबूझकर उस मान को वापस फेंकना है जो संदर्भ द्वारा कॉलिंग विधि के बारे में परवाह नहीं करता है। कुछ इस तरह:

// Assuming: void Person::GetNameAndAddr(std::string &name, std::string &addr);
string name;
person.GetNameAndAddr(name, string()); // don't care about addr

जैसा कि पिछले उत्तरों में बताया गया है, यह संकलन नहीं करता है। लेकिन यह संकलित करता है और सही ढंग से काम करता है (मेरे संकलक के साथ):

person.GetNameAndAddr(name,
    const_cast<string &>(static_cast<const string &>(string())));

यह सिर्फ दिखाता है कि आप संकलक से झूठ बोलने के लिए कास्टिंग का उपयोग कर सकते हैं। जाहिर है, अप्रयुक्त स्वचालित चर को घोषित करने और पारित करने के लिए यह बहुत साफ होगा:

string name;
string unused;
person.GetNameAndAddr(name, unused); // don't care about addr

यह तकनीक विधि के दायरे में एक अनावश्यक स्थानीय चर का परिचय देती है। यदि किसी कारण से आप इसे बाद में उपयोग करने से रोकना चाहते हैं, उदाहरण के लिए, भ्रम या त्रुटि से बचने के लिए, आप इसे स्थानीय ब्लॉक में छिपा सकते हैं:

string name;
{
    string unused;
    person.GetNameAndAddr(name, unused); // don't care about addr
}

- क्रिस


4

तुम कभी क्यों चाहोगे X& x = getx();? बस उपयोग करें X x = getx();और RVO पर भरोसा करें।


3
बिकॉज़ मैं g(getx())इसके बजाय कॉल करना चाहता हूंg(getx().ref())
एलेक्सी मालिस्टोव

4
@Alexey, यह एक वास्तविक कारण नहीं है। यदि आप ऐसा करते हैं, तो आपके पास एक तार्किक त्रुटि है, क्योंकि gआप किसी ऐसी चीज को संशोधित करने जा रहे हैं , जिस पर आप अपना हाथ नहीं बढ़ा सकते।
जोहान्स स्काउब -

3
@ जोहान्सचैब-लिट शायद वह परवाह नहीं करता।
जिज्ञासु

" आरवीओ पर भरोसा करें " को छोड़कर इसे "आरवीओ" नहीं कहा जाता है।
जिज्ञासु

2
@ कुरसीगुइ: यह इसके लिए एक बहुत ही स्वीकृत शब्द है। इसे "आरवीओ" के रूप में संदर्भित करने में कुछ भी गलत नहीं है।
०१

4

बुरे वर्कअराउंड में 'म्यूटेबल' कीवर्ड शामिल होता है। वास्तव में बुराई होना पाठक के लिए एक अभ्यास के रूप में छोड़ दिया जाता है। या यहाँ देखें: http://www.ddj.com/cpp/184403758


3

बहुत बढ़िया सवाल, और यहाँ एक और अधिक संक्षिप्त जवाब पर मेरा प्रयास है (क्योंकि बहुत उपयोगी जानकारी टिप्पणियों में है और शोर में खोदना मुश्किल है।)

किसी अस्थायी से सीधे जुड़ा कोई संदर्भ उसके जीवन का विस्तार करेगा [12.2.5]। दूसरी ओर, एक संदर्भ एक अन्य संदर्भ होगा साथ प्रारंभ नहीं (भले ही यह अंततः एक ही अस्थायी है)। यह समझ में आता है (संकलक को यह नहीं पता कि वह संदर्भ अंततः क्या संदर्भित करता है)।

लेकिन यह पूरा विचार बेहद भ्रामक है। उदाहरण के लिएconst X &x = X(); लिए जब तक xसंदर्भ के रूप में पिछले अस्थायी बना देगा , लेकिन const X &x = X().ref();नहीं होगा (जो जानता है कि क्याref() वास्तव में वापस आ गया है)। बाद के मामले में, Xइस लाइन के अंत में विध्वंसक को बुलाया जाता है। (यह एक गैर-तुच्छ विध्वंसक के साथ देखने योग्य है।)

तो यह आम तौर पर भ्रामक और खतरनाक लगता है (वस्तु जीवनकाल के बारे में नियमों को जटिल क्यों?), लेकिन संभवतः कम से कम संदर्भों की आवश्यकता थी, इसलिए मानक उनके लिए यह व्यवहार निर्धारित करता है।

[से Sbi टिप्पणी से]: ध्यान दें कि तथ्य यह है कि इसे एक कॉस्ट संदर्भ में बाँधना एक अस्थायी जीवनकाल को बढ़ाता है एक अपवाद है जिसे मैन्युअल रूप से जोड़ने के लिए जानबूझकर (TTBOMK) जोड़ा गया है। नॉन-कॉस्ट संदर्भों के लिए कोई अपवाद नहीं जोड़ा गया था, क्योंकि एक गैर-कॉस्ट-रेफरेंस को अस्थायी रूप से बाइंड करना एक प्रोग्रामर त्रुटि होने की सबसे अधिक संभावना थी।

सभी अस्थायी पूर्ण अभिव्यक्ति के अंत तक बने रहते हैं। हालांकि, उनका उपयोग करने के लिए, आपको एक चाल की आवश्यकता होती है जैसे आपके पास हैref() । वह कानूनी है। वहाँ अतिरिक्त घेरा के माध्यम से कूदने के लिए एक अच्छा कारण नहीं लगता है, प्रोग्रामर को याद दिलाने के अलावा कि कुछ असामान्य चल रहा है (अर्थात्, एक संदर्भ पैरामीटर जिसका संशोधन जल्दी से खो जाएगा)।

[एक और एसबीआई टिप्पणी] कारण स्ट्रॉन्स्ट्रुप (डी एंड ई में) गैर-कॉन्स्टेबल संदर्भों के अंतरालों के बंधन को अस्वीकार करने के लिए देता है, अगर एलेक्सी जी () एक वस्तु को संशोधित करेगा (जिसे आप एक गैर-कॉन्स्टेंस लेने वाले फ़ंक्शन से उम्मीद करेंगे। संदर्भ), यह एक ऐसी वस्तु को संशोधित करेगा जो मरने वाला है, इसलिए किसी को भी संशोधित मूल्य पर नहीं मिल सकता है। वह कहते हैं कि यह, सबसे अधिक संभावना है, एक त्रुटि है।


2

"यह स्पष्ट है कि अस्थायी वस्तु ऊपर के नमूने में स्थिर नहीं है, क्योंकि गैर-स्थिर कार्यों के लिए कॉल की अनुमति है। उदाहरण के लिए, रेफ () अस्थायी वस्तु को संशोधित कर सकता है।"

आपके उदाहरण में गेटएक्स () एक कॉन्स्टेंट एक्स को वापस नहीं करता है, इसलिए आप रेफ () को उसी तरह से कॉल करने में सक्षम हैं जैसे आप एक्स () रिफ। () को कॉल कर सकते हैं। आप एक नॉन कॉन्स्टैड रेफरी को लौटा रहे हैं और इसलिए नॉन कॉस्ट मैथड्स को कॉल कर सकते हैं, जो आप नहीं कर सकते हैं वह रेफरी को नॉन कॉस्ट रेफरेंस असाइन करें।

SadSidos टिप्पणी के साथ यह आपके तीन बिंदुओं को गलत बनाता है।


1

मेरे पास एक परिदृश्य है जिसे मैं साझा करना चाहूंगा जहां मैं चाहता हूं कि मैं वह कर सकता हूं जो एलेक्सी पूछ रहा है। माया C ++ प्लगइन में, मुझे नोड विशेषता में मान प्राप्त करने के लिए निम्नलिखित शेंनिगन करना होगा:

MFnDoubleArrayData myArrayData;
MObject myArrayObj = myArrayData.create(myArray);   
MPlug myPlug = myNode.findPlug(attributeName);
myPlug.setValue(myArrayObj);

यह लिखने के लिए कठिन है, इसलिए मैंने निम्नलिखित सहायक कार्य लिखे:

MPlug operator | (MFnDependencyNode& node, MObject& attribute){
    MStatus status;
    MPlug returnValue = node.findPlug(attribute, &status);
    return returnValue;
}

void operator << (MPlug& plug, MDoubleArray& doubleArray){
    MStatus status;
    MFnDoubleArrayData doubleArrayData;
    MObject doubleArrayObject = doubleArrayData.create(doubleArray, &status);
    status = plug.setValue(doubleArrayObject);
}

और अब मैं पोस्ट की शुरुआत से कोड लिख सकता हूं:

(myNode | attributeName) << myArray;

समस्या यह है कि यह दृश्य C ++ के बाहर संकलित नहीं होता है, क्योंकि यह अस्थायी चर को वापस करने की कोशिश कर रहा है | << ऑपरेटर के MPlug संदर्भ के लिए ऑपरेटर। मैं चाहूंगा कि यह एक संदर्भ हो क्योंकि इस कोड को कई बार कहा जाता है और मेरे पास MPlug की इतनी अधिक प्रतिलिपि नहीं है। मुझे केवल दूसरे फ़ंक्शन के अंत तक रहने के लिए अस्थायी ऑब्जेक्ट की आवश्यकता है।

खैर, यह मेरा परिदृश्य है। मैंने सोचा था कि मैं एक उदाहरण दिखाऊँगा जहाँ कोई ऐसा करना चाहेगा जो अलेक्सी वर्णन करे। मैं सभी आलोचकों और सुझावों का स्वागत करता हूँ!

धन्यवाद।

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