कोड उप-अभिव्यक्तियों के मूल्यांकन के अनिर्दिष्ट आदेश के कारण अनिर्दिष्ट व्यवहार प्रदर्शित करता है, हालांकि यह सभी कार्यों के भीतर होने वाले दुष्प्रभावों के बाद से अपरिभाषित व्यवहार को आमंत्रित नहीं करता है जो इस मामले में दुष्प्रभावों के बीच एक अनुक्रमण संबंध का परिचय देता है ।
इस उदाहरण में प्रस्ताव N4228 का उल्लेख किया गया है : Idiomatic C ++ के लिए शोधन अभिव्यक्ति मूल्यांकन आदेश जो प्रश्न में कोड के बारे में निम्नलिखित कहता है:
[...] इस कोड की समीक्षा C ++ विशेषज्ञों द्वारा विश्वव्यापी की गई है, और प्रकाशित (C ++ प्रोग्रामिंग लैंग्वेज, 4 वें संस्करण।) फिर भी, मूल्यांकन के अनिर्दिष्ट आदेश के लिए इसकी भेद्यता केवल एक उपकरण द्वारा खोजी गई है [।। ।]
विवरण
यह कई लोगों के लिए स्पष्ट हो सकता है कि फ़ंक्शन के तर्क में मूल्यांकन का अनिर्दिष्ट क्रम है, लेकिन यह संभवतः उतना स्पष्ट नहीं है कि यह व्यवहार जंजीर फ़ंक्शन कॉल के साथ कैसे इंटरैक्ट करता है। यह मेरे लिए स्पष्ट नहीं था जब मैंने पहली बार इस मामले का विश्लेषण किया था और स्पष्ट रूप से या तो सभी विशेषज्ञ समीक्षकों के लिए नहीं था।
पहली नज़र में यह प्रतीत हो सकता है कि चूंकि प्रत्येक replace
को बाएं से दाएं मूल्यांकन किया जाना है कि संबंधित फ़ंक्शन तर्क समूहों का मूल्यांकन बाएं से दाएं समूहों के रूप में भी किया जाना चाहिए।
यह गलत है, फ़ंक्शन तर्कों का मूल्यांकन का अनिर्दिष्ट क्रम है, हालांकि फंक्शन कॉलिंग चैन प्रत्येक फ़ंक्शन कॉल के लिए बाएं से दाएं मूल्यांकन क्रम का परिचय देता है, प्रत्येक फ़ंक्शन कॉल के तर्क केवल सदस्य फ़ंक्शन कॉल के संबंध में अनुक्रमित होते हैं। का। विशेष रूप से यह निम्नलिखित कॉल को प्रभावित करता है:
s.find( "even" )
तथा:
s.find( " don't" )
जो अनिश्चित काल के लिए सम्मान के साथ अनुक्रमित हैं:
s.replace(0, 4, "" )
दो find
कॉल का मूल्यांकन इससे पहले या बाद में किया जा सकता है replace
, जो मायने रखता है क्योंकि यह s
एक तरह से साइड इफेक्ट है जो परिणाम को बदल देगा find
, यह लंबाई को बदलता है s
। इसलिए जब replace
दोनों find
कॉल के सापेक्ष मूल्यांकन किया जाता है तो परिणाम भिन्न होगा।
यदि हम जंजीर की अभिव्यक्ति को देखते हैं और कुछ उप-अभिव्यक्तियों के मूल्यांकन क्रम की जांच करते हैं:
s.replace(0, 4, "" ).replace( s.find( "even" ), 4, "only" )
^ ^ ^ ^ ^ ^ ^ ^ ^
A B | | | C | | |
1 2 3 4 5 6
तथा:
.replace( s.find( " don't" ), 6, "" );
^ ^ ^ ^
D | | |
7 8 9
ध्यान दें, हम इस तथ्य की अनदेखी कर रहे हैं कि 4
और 7
अधिक उप-अभिव्यक्तियों में टूट सकता है। इसलिए:
A
इससे पहले B
जो अनुक्रम C
किया जाता है उससे पहले अनुक्रम किया जाता हैD
1
के 9
धुंधलेपन से नीचे सूचीबद्ध अपवादों में से कुछ के साथ अन्य उप भाव के संबंध में अनुक्रम कर रहे हैं
1
से 3
पहले सिलवाया जाता हैB
4
से 6
पहले सिलवाया जाता हैC
7
से 9
पहले सिलवाया जाता हैD
इस मुद्दे की कुंजी यह है कि:
4
करने के 9
लिए सम्मान के साथ अनिश्चित काल के अनुक्रम हैंB
के लिए मूल्यांकन पसंद के संभावित आदेश 4
और 7
के संबंध में B
के बीच परिणामों में अंतर बताते हैं clang
और gcc
जब का मूल्यांकन f2()
। मेरी परीक्षणों में clang
मूल्यांकन करता है B
मूल्यांकन करने से पहले 4
और 7
जबकि gcc
यह मूल्यांकन करता है के बाद। प्रत्येक मामले में क्या हो रहा है, यह प्रदर्शित करने के लिए हम निम्नलिखित परीक्षण कार्यक्रम का उपयोग कर सकते हैं:
#include <iostream>
#include <string>
std::string::size_type my_find( std::string s, const char *cs )
{
std::string::size_type pos = s.find( cs ) ;
std::cout << "position " << cs << " found in complete expression: "
<< pos << std::endl ;
return pos ;
}
int main()
{
std::string s = "but I have heard it works even if you don't believe in it" ;
std::string copy_s = s ;
std::cout << "position of even before s.replace(0, 4, \"\" ): "
<< s.find( "even" ) << std::endl ;
std::cout << "position of don't before s.replace(0, 4, \"\" ): "
<< s.find( " don't" ) << std::endl << std::endl;
copy_s.replace(0, 4, "" ) ;
std::cout << "position of even after s.replace(0, 4, \"\" ): "
<< copy_s.find( "even" ) << std::endl ;
std::cout << "position of don't after s.replace(0, 4, \"\" ): "
<< copy_s.find( " don't" ) << std::endl << std::endl;
s.replace(0, 4, "" ).replace( my_find( s, "even" ) , 4, "only" )
.replace( my_find( s, " don't" ), 6, "" );
std::cout << "Result: " << s << std::endl ;
}
परिणाम gcc
( इसे लाइव देखें )
position of even before s.replace(0, 4, "" ): 26
position of don't before s.replace(0, 4, "" ): 37
position of even after s.replace(0, 4, "" ): 22
position of don't after s.replace(0, 4, "" ): 33
position don't found in complete expression: 37
position even found in complete expression: 26
Result: I have heard it works evenonlyyou donieve in it
परिणाम clang
( इसे लाइव देखें ):
position of even before s.replace(0, 4, "" ): 26
position of don't before s.replace(0, 4, "" ): 37
position of even after s.replace(0, 4, "" ): 22
position of don't after s.replace(0, 4, "" ): 33
position even found in complete expression: 22
position don't found in complete expression: 33
Result: I have heard it works only if you believe in it
परिणाम Visual Studio
( इसे लाइव देखें ):
position of even before s.replace(0, 4, "" ): 26
position of don't before s.replace(0, 4, "" ): 37
position of even after s.replace(0, 4, "" ): 22
position of don't after s.replace(0, 4, "" ): 33
position don't found in complete expression: 37
position even found in complete expression: 26
Result: I have heard it works evenonlyyou donieve in it
मानक से विवरण
हम जानते हैं कि जब तक उप-अभिव्यक्तियों के मूल्यांकन को निर्दिष्ट नहीं किया जाता है, यह C ++ 11 मानक अनुभाग 1.9
प्रोग्राम निष्पादन के मसौदे से है जो निम्न है:
जहां नोट किया गया है, सिवाय इसके कि अलग-अलग संचालकों के संचालनों का मूल्यांकन और अलग-अलग अभिव्यक्तियों के उप-संदर्भों का मूल्यांकन न किया जाए। […]
और हम जानते हैं कि एक फ़ंक्शन कॉल फ़ंक्शन के संबंध से पहले एक अनुक्रम का परिचय देता है, जो फ़ंक्शन बॉडी के संबंध में पोस्टफ़िक्स अभिव्यक्ति और तर्कों को अनुभाग से कहता है 1.9
:
[...] एक फ़ंक्शन को कॉल करते समय (फ़ंक्शन इनलाइन है या नहीं), किसी भी तर्क अभिव्यक्ति के साथ जुड़े प्रत्येक मूल्य संगणना और साइड इफेक्ट, या पोस्ट फ़ंक्शन को पोस्ट फ़ंक्शन को नामित करने वाले अभिव्यक्ति के साथ, प्रत्येक अभिव्यक्ति या कथन के निष्पादन से पहले अनुक्रमित किया जाता है। कहा समारोह के शरीर में [...]
हम यह भी जानते हैं कि क्लास मेंबर एक्सेस और इसलिए चैनिंग, 5.2.5
क्लास क्लास मेंबर एक्सेस से लेफ्ट से राइट की ओर मूल्यांकन करेगा, जो कहता है:
[...] बिंदु या तीर से पहले उपसर्ग अभिव्यक्ति का मूल्यांकन किया जाता है; 64
उस मूल्यांकन का परिणाम, आईडी-एक्सप्रेशन के साथ, पूरे पोस्टफिक्स एक्सप्रेशन के परिणाम को निर्धारित करता है।
ध्यान दें, उस स्थिति में जहां आईडी-एक्सप्रेशन एक गैर-स्थैतिक सदस्य फ़ंक्शन के रूप में समाप्त होता है, यह अभिव्यक्ति-सूची के मूल्यांकन के आदेश को निर्दिष्ट नहीं करता है ()
क्योंकि यह एक अलग उप-अभिव्यक्ति है। 5.2
पोस्टफ़िक्स अभिव्यक्तियों से संबंधित व्याकरण :
postfix-expression:
postfix-expression ( expression-listopt) // function call
postfix-expression . templateopt id-expression // Class member access, ends
// up as a postfix-expression
C ++ 17 परिवर्तन
प्रस्ताव p0145r3: परिष्कृत अभिव्यक्ति मूल्यांकन के लिए मुहावरेदार सी ++ ने कई बदलाव किए। पोस्टफ़िक्स-एक्सप्रेशन और उनकी अभिव्यक्ति-सूची के लिए मूल्यांकन नियमों के क्रम को मजबूत करके कोड को अच्छी तरह से निर्दिष्ट व्यवहार देने वाले परिवर्तन शामिल हैं ।
[expr.call] p5 कहते हैं:
अभिव्यक्ति-सूची और किसी भी डिफ़ॉल्ट तर्क में प्रत्येक अभिव्यक्ति से पहले उपसर्ग-अभिव्यक्ति अनुक्रमित है । हर संबंधित मूल्य संगणना और साइड इफेक्ट सहित एक पैरामीटर का आरम्भ, अनिश्चित रूप से किसी अन्य पैरामीटर के संबंध में अनुक्रमित है। [ध्यान दें: फ़ंक्शन दर्ज किए जाने से पहले तर्क मूल्यांकन के सभी दुष्प्रभाव अनुक्रमित हैं (देखें 4.6)। ध्यान दें] [उदाहरण:
void f() {
std::string s = "but I have heard it works even if you don’t believe in it";
s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don’t"), 6, "");
assert(s == "I have heard it works only if you believe in it"); // OK
}
उदाहरण का]
s.replace( s.replace( s.replace(0, 4, "" ).find( "even" ), 4, "only" ).find( " don't" ), 6, "" );