सी + + निष्पादन व्यवस्था विधि जंजीरों में


108

इस कार्यक्रम का उत्पादन:

#include <iostream> 
class c1
{   
  public:
    c1& meth1(int* ar) {
      std::cout << "method 1" << std::endl;
      *ar = 1;
      return *this;
    }
    void meth2(int ar)
    {
      std::cout << "method 2:"<< ar << std::endl;
    }
};

int main()
{
  c1 c;
  int nu = 0;
  c.meth1(&nu).meth2(nu);
}

है:

method 1
method 2:0

nuजब meth2()शुरू होता है तो 1 क्यों नहीं है ?


41
@MartinBonner: हालांकि मुझे जवाब पता है, मैं इसे "स्पष्ट" शब्द के किसी भी अर्थ में नहीं कहूंगा और अगर ऐसा होता भी है, तो यह ड्राइव करने के लिए एक सभ्य कारण नहीं होगा। निराशाजनक!
ऑर्बिट में लाइटनेस दौड़

4
यह तब होता है जब आप अपने तर्कों को संशोधित करते हैं। उनके तर्कों को संशोधित करने वाले कार्य पढ़ने में कठिन हैं, अगले प्रोग्रामर के लिए कोड पर काम करने के लिए उनके प्रभाव अप्रत्याशित हैं और वे इसके लिए आश्चर्यचकित होते हैं। मैं दृढ़ता से इनवॉइस को छोड़कर किसी भी पैरामीटर को संशोधित करने से बचने का सुझाव देता हूं। इनवोक को संशोधित करने से यहां कोई समस्या नहीं होगी, क्योंकि दूसरी विधि को पहले के परिणाम पर कहा जाता है, इसलिए इस पर प्रभाव का आदेश दिया जाता है। अभी भी कुछ मामले हैं जहां वे हालांकि नहीं होंगे।
जान हुदेक


@ जानहुडेक यही कारण है कि कार्यात्मक प्रोग्रामिंग फ़ंक्शन शुद्धता पर इतना बड़ा जोर देती है।
फाप्र्स

2
उदाहरण के लिए, एक ढेर आधारित बुला सम्मेलन शायद धक्का करना पसंद करेंगे nu, &nuऔर cढेर करने के लिए इसी क्रम में पर है, तो आह्वान meth1, ढेर करने पर परिणाम धक्का, तो आह्वान meth2है, जबकि एक रजिस्टर आधारित बुला सम्मेलन चाहेगा लोड cऔर &nuरजिस्टर में, आह्वान meth1, nuएक रजिस्टर में लोड , फिर आह्वान meth2
नील

जवाबों:


66

क्योंकि मूल्यांकन आदेश अनिर्दिष्ट है।

आप को बुलाया जाने से पहले मूल्यांकन nuकिया mainजा रहा है। जंजीर के साथ यही समस्या है। मैं इसे नहीं करने की सलाह देता हूं।0meth1

बस एक अच्छा, सरल, स्पष्ट, आसानी से पढ़ा जाने वाला, आसानी से समझने वाला कार्यक्रम बनाएं:

int main()
{
  c1 c;
  int nu = 0;
  c.meth1(&nu);
  c.meth2(nu);
}

14
एक संभावना है, कि कुछ मामलों में मूल्यांकन आदेश को स्पष्ट करने का एक प्रस्ताव , जो इस समस्या को ठीक करता है, C ++ 17 के लिए आएगा
Revolver_Ocelot

7
मुझे मेथड चाइनिंग पसंद है (उदाहरण के <<लिए आउटपुट के लिए, और "ऑब्जेक्ट बिल्डर्स" कॉम्प्लेक्स ऑब्जेक्ट्स के लिए बहुत सारे तर्कों के साथ - लेकिन यह वास्तव में आउटपुट तर्कों के साथ बहुत बुरी तरह से मिश्रण करता है।
मार्टिन बोनर मोनिका

34
क्या मैं इसे सही समझता हूं? के मूल्यांकन के क्रम meth1और meth2परिभाषित किया गया है, लेकिन के लिए पैरामीटर के मूल्यांकन meth2से पहले हो सकता है meth1कहा जाता है ...?
रोडी

7
जब तक विधियां समझदार होती हैं तब तक विधि-निर्धारण ठीक होता है और केवल इनवोकेंट को संशोधित करते हैं (जिसके लिए प्रभावों को अच्छी तरह से आदेश दिया जाता है, क्योंकि दूसरी विधि को पहले के परिणाम पर कहा जाता है)।
जन हडेक

4
यह तर्कसंगत है, जब आप इसके बारे में सोचते हैं। यह काम करता हैmeth2(meth1(c, &nu), nu)
बार्टेकचोम

29

मुझे लगता है कि मूल्यांकन के आदेश के बारे में मसौदा मानक का यह हिस्सा प्रासंगिक है:

1.9 कार्यक्रम निष्पादन

...

  1. जहां नोट किया गया है, सिवाय इसके कि अलग-अलग संचालकों के संचालनों का मूल्यांकन और व्यक्तिगत अभिव्यक्तियों के उप-वर्गों के मूल्यांकन का परिणाम नहीं है। ऑपरेटर के परिणाम के मान की गणना संचालक के परिणाम की गणना के पहले की जाती है। यदि एक स्केलर ऑब्जेक्ट पर एक साइड इफेक्ट एक समान स्केलर ऑब्जेक्ट पर या तो एक साइड इफेक्ट या एक ही स्केलर ऑब्जेक्ट के मूल्य का उपयोग करके मूल्य गणना के सापेक्ष अप्रयुक्त है, और वे संभावित समवर्ती नहीं हैं, तो व्यवहार अपरिभाषित है

और भी:

5.2.2 फंक्शन कॉल

...

  1. [नोट: उपसर्ग अभिव्यक्ति और तर्कों के मूल्यांकन सभी एक दूसरे के सापेक्ष परिणामहीन हैं। तर्क के मूल्यांकन के सभी दुष्प्रभाव समारोह में प्रवेश करने से पहले अनुक्रमित होते हैं - अंतिम नोट]

तो अपनी लाइन के c.meth1(&nu).meth2(nu);लिए, अंतिम कॉल के लिए फ़ंक्शन कॉल ऑपरेटर के संदर्भ में ऑपरेटर में क्या हो रहा है, इस पर विचार करें meth2, इसलिए हम स्पष्ट रूप से पोस्टफिक्स अभिव्यक्ति और तर्क में टूट को देखते हैं nu:

operator()(c.meth1(&nu).meth2, nu);

अंतिम फ़ंक्शन कॉल के लिए पोस्टफ़िक्स एक्सप्रेशन और तर्क का मूल्यांकन (यानी पोस्टफ़िक्स एक्सप्रेशन c.meth1(&nu).meth2और nu) उपरोक्त फ़ंक्शन कॉल नियम के अनुसार एक दूसरे के सापेक्ष अनियोजित हैं । इसलिए, स्केलर ऑब्जेक्ट पर पोस्टफिक्स एक्सप्रेशन की गणना के साइड-इफेक्ट को फ़ंक्शन कॉल से पहले के तर्क मूल्यांकन के सापेक्ष अनलोड किया जाता है । द्वारा प्रोग्राम निष्पादन ऊपर शासन, इस अपरिभाषित व्यवहार है।arnumeth2

दूसरे शब्दों में, कॉल के बाद कॉल nuकरने के लिए तर्क का मूल्यांकन करने के लिए कंपाइलर की कोई आवश्यकता नहीं है - यह मूल्यांकन को प्रभावित करने के लिए कोई दुष्प्रभाव नहीं मानने के लिए स्वतंत्र है ।meth2meth1meth1nu

उपरोक्त द्वारा निर्मित असेंबली कोड में mainफ़ंक्शन में निम्नलिखित अनुक्रम होते हैं :

  1. वैरिएबल nuको स्टैक पर आवंटित किया गया है और 0 से आरम्भ किया गया है।
  2. एक रजिस्टर ( ebxमेरे मामले में) के मूल्य की एक प्रति प्राप्त करता हैnu
  3. के पते nuऔर cपैरामीटर रजिस्टर में लोड किए गए हैं
  4. meth1 कहा जाता है
  5. वापसी मान रजिस्टर और पहले से कैश की गई मूल्य की nuमें ebxरजिस्टर पैरामीटर रजिस्टरों में लोड किए गए हैं
  6. meth2 कहा जाता है

गंभीर रूप से, संकलक के ऊपर चरण 5 में, nuचरण 2 से कैश्ड मान को फ़ंक्शन कॉल में पुन: उपयोग करने की अनुमति देता है meth2। यहाँ यह उस संभावना की अवहेलना करता nuहै जिसे कॉल द्वारा परिवर्तित किया जा सकता है meth1- कार्रवाई में 'अपरिभाषित व्यवहार'।

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


2
ये गलत है। फ़ंक्शन कॉल कॉल फ़ंक्शन में अनिश्चित रूप से अनुक्रमित w / r / t अन्य मूल्यांकन हैं (जब तक कि एक अनुक्रम-पहले बाधा अन्यथा लागू नहीं होती है); वे इंटरलेव नहीं करते हैं।
टीसी

1
@TC - मैंने फ़ंक्शन कॉल के बारे में कभी भी कुछ नहीं कहा। मैंने केवल ऑपरेटरों के साइड-इफेक्ट्स का उल्लेख किया है। यदि आप उपरोक्त द्वारा निर्मित असेंबली कोड को देखते हैं, तो आप देखेंगे कि meth1इसे पहले निष्पादित किया गया है meth2, लेकिन इसके लिए पैरामीटर को कॉल करने से पहले एक रजिस्टर में कैश किया गया meth2मान nuहै meth1- अर्थात संकलक ने संभावित दुष्प्रभावों की अनदेखी की है, जो है मेरे उत्तर के अनुरूप।
स्माइही

1
आप बिल्कुल दावा कर रहे हैं कि - "इसका साइड-इफ़ेक्ट (यानी ar का मान सेट करना) कॉल से पहले अनुक्रमित होने की गारंटी नहीं है"। फ़ंक्शन कॉल में पोस्टफ़िक्स-एक्सप्रेशन का मूल्यांकन (जो है c.meth1(&nu).meth2) और उस कॉल के तर्क का मूल्यांकन ( nu) आम तौर पर अनियोजित हैं, लेकिन 1) उनके साइड इफेक्ट्स सभी में प्रवेश करने से पहले अनुक्रमित हैं meth2और 2) चूंकि c.meth1(&nu)एक फ़ंक्शन कॉल है , यह अनिश्चित काल के मूल्यांकन के साथ अनुक्रमित है nu। अंदर meth2, अगर यह किसी तरह से चर में एक पॉइंटर प्राप्त करता है, तो यह mainहमेशा 1 दिखाई देगा
टीसी

2
"हालांकि, ऑपरेंड्स की गणना का साइड-इफ़ेक्ट (यानी मान को सेट करना) ऊपर कुछ भी करने से पहले अनुक्रमित होने की गारंटी नहीं है (2 प्रति के अनुसार)।" यह पूरी तरह से कॉल करने से पहले अनुक्रमित होने की गारंटी है meth2, जैसा कि आपके द्वारा उद्धृत किए जा रहे cppreference पेज के आइटम 3 में उल्लेखित है (जिसे आपने ठीक से उद्धृत करने के लिए उपेक्षित भी किया है)।
TC

1
आपने कुछ गलत किया, और इसे बदतर बना दिया। यहाँ बिल्कुल अपरिभाषित व्यवहार नहीं है। उदाहरण के लिए [intro.execution] / 15 पढ़ते रहें।
टीसी

9

1998 सी ++ मानक में, धारा 5, पैरा 4

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

(मैंने फुटनोट # 53 के संदर्भ को छोड़ दिया है जो इस प्रश्न के लिए प्रासंगिक नहीं है)।

अनिवार्य रूप से, &nuकॉल करने से पहले मूल्यांकन किया जाना चाहिए c1::meth1(), और nuकॉल करने से पहले मूल्यांकन किया जाना चाहिए c1::meth2()। हालाँकि, कोई आवश्यकता नहीं है जिसका nuपहले मूल्यांकन किया गया हो &nu(जैसे कि यह अनुमति है कि nuपहले मूल्यांकन किया जाए, फिर &nu, और फिर c1::meth1()कहा जाता है - जो कि आपका कंपाइलर कर रहा है)। अभिव्यक्ति *ar = 1में c1::meth1()इसलिए इसकी गारंटी नहीं है इससे पहले मूल्यांकन किया जाना nuमें main()मूल्यांकन किया जाता है, क्रम में करने के लिए भेजा जा सके c1::meth2()

बाद में C ++ मानक (जो कि मेरे पास वर्तमान में पीसी पर नहीं है जो मैं आज रात उपयोग कर रहा हूं) में अनिवार्य रूप से एक ही खंड है।


7

मुझे लगता है कि जब संकलन किया जाता है, तो कथनों से पहले meth1 और meth2 को वास्तव में कहा जाता है, पैरामाटर्स को उनके पास भेज दिया गया है। मेरा मतलब है जब आप "c.meth1 (& nu) .meth2 (nu)" का उपयोग करते हैं; मान nu = 0 को meth2 में पास कर दिया गया है, इसलिए इससे कोई फर्क नहीं पड़ता कि "nu" बाद में बदला गया है।

आप यह कोशिश कर सकते हैं:

#include <iostream> 
class c1
{
public:
    c1& meth1(int* ar) {
        std::cout << "method 1" << std::endl;
        *ar = 1;
        return *this;
    }
    void meth2(int* ar)
    {
        std::cout << "method 2:" << *ar << std::endl;
    }
};

int main()
{
    c1 c;
    int nu = 0;
    c.meth1(&nu).meth2(&nu);
    getchar();
}

इसका उत्तर आपको चाहिए

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