कोड के इस स्निपेट में "2 + 3 = 15" प्रिंट क्यों होता है?


126

नीचे दिए गए प्रोग्राम का आउटपुट क्यों है?

#include <iostream>
using namespace std;

int main(){

    cout << "2+3 = " <<
    cout << 2 + 3 << endl;
}

पैदा करता है

2+3 = 15

उम्मीद के बजाय

2+3 = 5

यह सवाल पहले ही कई करीबी / रीओपन चक्रों में चला गया है।

मतदान बंद करने से पहले, कृपया इस मुद्दे के बारे में मेटा चर्चा पर विचार करें


96
आप ;पहली आउटपुट लाइन के अंत में अर्धविराम चाहते हैं , नहीं <<। आप वो नहीं छाप रहे हैं जो आपको लगता है कि आप छाप रहे हैं। आप कर रहे हैं cout << cout, जो प्रिंट 1(यह उपयोग करता है cout.operator bool(), मुझे लगता है)। तब 5(से 2+3) तुरंत अनुसरण करता है, जिससे यह पंद्रह की संख्या जैसा दिखता है।
इगोर तांडेटनिक

5
@StephanLechner शायद gcc4 का उपयोग कर रहा है। उनके पास gcc5 तक विशेष रूप से पूरी तरह से सुसंगत धाराएं नहीं थीं, विशेष रूप से, तब भी उनके पास तब तक रूपांतरण निहित था।
बौम Augen mit

4
@IgorTandetnik जो किसी उत्तर की शुरुआत की तरह लगता है। इस सवाल पर बहुत सारी सूक्ष्मताएं दिखाई देती हैं जो पहले पढ़ने पर स्पष्ट नहीं होती हैं।
मार्क रैनसम

14
लोग इस प्रश्न को बंद करने के लिए मतदान क्यों करते हैं? यह नहीं है "कृपया मुझे बताएं कि इस कोड में क्या गलत है," लेकिन "यह कोड इस आउटपुट का उत्पादन क्यों करता है?" पहले का जवाब "आपने एक टाइपो बनाया है", हाँ, लेकिन दूसरे को एक स्पष्टीकरण की आवश्यकता है कि कंपाइलर कोड की व्याख्या कैसे कर रहा है, यह एक कंपाइलर त्रुटि क्यों नहीं है, और यह पॉइंटर एड्रेस के बजाय "1" कैसे हो रहा है।
jaggedSpire

6
@jaggedSpire यदि यह एक टाइपोग्राफिक त्रुटि नहीं है, तो यह एक बहुत बुरा सवाल है क्योंकि तब यह जानबूझकर एक असामान्य निर्माण का उपयोग करता है जो कि जानबूझकर किए बिना टाइपोग्राफिक त्रुटि की तरह दिखता है। किसी भी तरह, एक करीबी वोट के हकदार हैं। (या तो एक टाइपोग्राफिक त्रुटि या खराब / दुर्भावनापूर्ण होने के कारण। यह लोगों की मदद के लिए एक साइट है, न कि लोगों को दूसरों को बरगलाने के लिए।)
डेविड श्वार्ट्ज

जवाबों:


229

जानबूझकर या दुर्घटना से, आपके पास <<पहली आउटपुट लाइन के अंत में है, जहां आप शायद मतलब रखते थे ;। तो आप अनिवार्य रूप से है

cout << "2+3 = ";  // this, of course, prints "2+3 = "
cout << cout;      // this prints "1"
cout << 2 + 3;     // this prints "5"
cout << endl;      // this finishes the line

तो सवाल इस पर उबलता है: cout << cout;प्रिंट क्यों करता है "1"?

यह पता चला है, शायद आश्चर्यजनक रूप से, सूक्ष्म रूप से। std::cout, इसके आधार वर्ग के माध्यम से std::basic_ios, एक निश्चित प्रकार का रूपांतरण ऑपरेटर प्रदान करता है जिसका उद्देश्य बूलियन संदर्भ में उपयोग किया जाना है

while (cout) { PrintSomething(cout); }

यह एक बहुत ही खराब उदाहरण है, क्योंकि असफल होने के लिए आउटपुट प्राप्त करना मुश्किल है - लेकिन std::basic_iosवास्तव में इनपुट और आउटपुट स्ट्रीम दोनों के लिए एक आधार वर्ग है, और इनपुट के लिए यह बहुत अधिक समझ में आता है:

int value;
while (cin >> value) { DoSomethingWith(value); }

(धारा के अंत में लूप से बाहर हो जाता है, या जब धारा वर्ण एक वैध पूर्णांक नहीं बनाते हैं)।

अब, इस रूपांतरण ऑपरेटर की सटीक परिभाषा मानक के C ++ 03 और C ++ 11 संस्करणों के बीच बदल गई है। पुराने संस्करणों में, इसे operator void*() const;(आमतौर पर लागू किया गया था return fail() ? NULL : this;), जबकि नए में यह explicit operator bool() const;(आमतौर पर बस के रूप में लागू किया गया है return !fail();)। दोनों घोषणाएं एक बूलियन संदर्भ में ठीक काम करती हैं, लेकिन इस तरह के संदर्भ के बाहर इस्तेमाल किए जाने पर (गलत) अलग व्यवहार करती हैं।

विशेष रूप से, C ++ 03 नियमों के तहत, कुछ पते के cout << coutरूप में व्याख्या की जाएगी cout << cout.operator void*()। C ++ 11 नियमों के तहत, cout << coutसभी को संकलित नहीं करना चाहिए, क्योंकि ऑपरेटर घोषित किया गया है explicitऔर इस प्रकार निहित रूपांतरण में भाग नहीं ले सकता है। यह वास्तव में परिवर्तन के लिए प्राथमिक प्रेरणा थी - निरर्थक कोड को संकलन से रोकना। एक कंपाइलर जो किसी भी मानक के अनुरूप होता है, वह एक प्रोग्राम का उत्पादन नहीं करता है जो प्रिंट करता है "1"

जाहिरा तौर पर, कुछ C ++ क्रियान्वयन इस तरह से कंपाइलर और लाइब्रेरी को मिलाने और मिलान करने की अनुमति देते हैं, जो गैर-अनुरूप परिणाम उत्पन्न करते हैं (@StephanLechner को उद्धृत करते हुए: "मुझे एक सेटिंग मिली xcode में जो 1 का उत्पादन करता है, और दूसरा वह है जो एक पता देता है: भाषा बोली c ++ 98 को "मानक पुस्तकालय libc ++ (LLV मानक पुस्तकालय के साथ c ++ 11 समर्थन)" पैदावार 1 के साथ जोड़ा गया है, जबकि c ++ 98 libstdc के साथ संयुक्त है (gnu c ++ मानक पुस्तकालय) एक पता देता है; ")। आपके पास एक C ++ 03-स्टाइल कंपाइलर हो सकता है explicitजो कनवर्ज़न ऑपरेटर्स (जो C ++ 11 में नए हैं) को C ++ 11-स्टाइल लाइब्रेरी के साथ संयुक्त नहीं करता है जो रूपांतरण को परिभाषित करता है operator bool()। इस तरह के मिश्रण के साथ, इसकी cout << coutव्याख्या करना संभव हो जाता है cout << cout.operator bool(), जो बदले में बस cout << trueऔर प्रिंट होता है "1"


1
@ मुझे पूरा यकीन है कि इस विशेष क्षेत्र में C ++ 03 और C ++ 98 के बीच कोई अंतर नहीं है। मुझे लगता है कि मैं C ++ 03 के सभी उल्लेखों को "प्री-सी ++ 11" से बदल सकता हूं, अगर इससे मामलों को स्पष्ट करने में मदद मिलेगी। मैं लिनक्स एट अल पर संकलक और पुस्तकालय संस्करण की पेचीदगियों से बिल्कुल परिचित नहीं हूं; मैं एक विंडोज़ / MSVC आदमी हूँ।
इगोर टांडेटनिक

4
मैं C ++ 03 और C ++ 98 के बीच नाइटपिक करने की कोशिश नहीं कर रहा था; मुद्दा यह है कि libc ++ केवल C ++ 11 और नया है; यह C ++ 98/03 के अनुरूप होने का प्रयास नहीं करता है।
टीसी

45

इगोर कहते हैं, आप एक सी ++ 11 पुस्तकालय है, जहां के साथ इस मिल std::basic_iosगया है operator boolके बजाय operator void*, लेकिन किसी भी तरह की घोषणा नहीं कर रहा है (या के रूप में इलाज) explicit। सही घोषणा के लिए यहां देखें ।

उदाहरण के लिए, एक अनुरूप C ++ 11 संकलक एक ही परिणाम देगा

#include <iostream>
using namespace std;

int main() {
    cout << "2+3 = " << 
    static_cast<bool>(cout) << 2 + 3 << endl;
}

लेकिन आपके मामले में, एक static_cast<bool>गलत रूपांतरण के रूप में अनुमति दी जा रही है (गलत तरीके से)।


संपादित करें: चूंकि यह सामान्य या अपेक्षित व्यवहार नहीं है, इसलिए यह आपके मंच, संकलक संस्करण आदि को जानने के लिए उपयोगी हो सकता है।


संपादन 2: संदर्भ के लिए, कोड आमतौर पर या तो लिखा जाएगा

    cout << "2+3 = "
         << 2 + 3 << endl;

या के रूप में

    cout << "2+3 = ";
    cout << 2 + 3 << endl;

और यह दो शैलियों को एक साथ मिला रहा है जो बग को उजागर करते हैं।


1
आपके पहले सुझाए गए समाधान कोड में एक टाइपो है। एक बहुत सारे ऑपरेटर।
एरोरिका

3
अब मैं यह कर रहा हूँ, यह संक्रामक होना चाहिए। धन्यवाद!
बेकार

1
हा! :) अपने उत्तर के प्रारंभिक संपादन में, मैंने अर्धविराम को जोड़ने का सुझाव दिया, लेकिन लाइन के अंत में ऑपरेटर को महसूस नहीं किया। मुझे लगता है कि ओपी के साथ, हमने टाइपोस के सबसे महत्वपूर्ण क्रम को उत्पन्न किया है।
एरोरिका

21

अप्रत्याशित उत्पादन का कारण एक टाइपो है। आपका शायद मतलब था

cout << "2+3 = "
     << 2 + 3 << endl;

यदि हम उन स्ट्रिंग्स को अनदेखा करते हैं जिनमें अपेक्षित आउटपुट है, तो हम साथ रह जाते हैं:

cout << cout;

C ++ 11 के बाद से, यह बीमार है। std::coutकुछ भी std::basic_ostream<char>::operator<<(या एक गैर सदस्य अधिभार) को स्पष्ट रूप से परिवर्तनीय नहीं होगा। इसलिए कंपाइलर के अनुरूप मानकों को ऐसा करने के लिए आपको कम से कम चेतावनी देनी चाहिए। मेरे संकलक ने आपके कार्यक्रम को संकलित करने से इनकार कर दिया।

std::coutकरने के लिए परिवर्तनीय होगा bool, और स्ट्रीम इनपुट ऑपरेटर के बूल अधिभार में 1 का मनाया उत्पादन होगा। हालांकि, यह अधिभार स्पष्ट है, इसलिए यह एक अंतर्निहित रूपांतरण की अनुमति नहीं देना चाहिए। ऐसा प्रतीत होता है कि आपका संकलक / मानक पुस्तकालय कार्यान्वयन कड़ाई से मानक के अनुरूप नहीं है।

प्री-सी ++ 11 मानक में, यह अच्छी तरह से बनता है। इसके बाद std::coutएक अंतर्निहित रूपांतरण ऑपरेटर होता है void*जिसमें एक स्ट्रीम इनपुट ऑपरेटर अधिभार होता है। हालांकि इसके लिए आउटपुट अलग होगा। यह std::coutऑब्जेक्ट का मेमोरी एड्रेस प्रिंट करेगा ।


11

पोस्ट किए गए कोड को किसी भी C ++ 11 (या बाद के अनुरूप कंपाइलर) के लिए संकलित नहीं किया जाना चाहिए, लेकिन इसे पूर्व C ++ 11 कार्यान्वयन पर चेतावनी के बिना भी संकलन करना चाहिए।

अंतर यह है कि C ++ 11 ने एक धारा के रूपांतरण को एक बूल स्पष्ट किया है:

C.2.15 क्लाज 27: इनपुट / आउटपुट लाइब्रेरी [diff.cpp03.input.output] 27.7.2.1.3, 27.7.3.4, 27.5.5.4

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

  • एक फ़ंक्शन का मान पास करना जो टाइप बूल का तर्क लेता है;
    ...

ओस्ट्रीम ऑपरेटर << एक बूल पैरामीटर के साथ परिभाषित किया गया है। बूल में रूपांतरण अस्तित्व में था (और स्पष्ट नहीं था) पूर्व-सी ++ 11 है, cout << coutजिसका अनुवाद cout << true1 किया गया।

और C.2.15 के अनुसार, यह अब C ++ 11 से शुरू नहीं होना चाहिए।


3
boolC ++ 03 में कोई भी रूपांतरण मौजूद नहीं है , हालांकि std::basic_ios::operator void*()एक सशर्त या लूप की नियंत्रित अभिव्यक्ति के रूप में सार्थक है।
बेन वोइगट

7

आप आसानी से अपने कोड को इस तरह डिबग कर सकते हैं। जब आप coutअपने आउटपुट का उपयोग करते हैं, तो आप इसका विश्लेषण इस तरह कर सकते हैं:

पहले coutबफ़र का प्रतिनिधित्व करने की कल्पना करें और ऑपरेटर बफ़र <<के अंत में आने का प्रतिनिधित्व करता है। ऑपरेटर <<का परिणाम आउटपुट स्ट्रीम है, आपके मामले में cout। आप से शुरू:

cout << "2+3 = " << cout << 2 + 3 << endl;

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

buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);

जैसा कि मैंने कहा कि परिणाम के पहले buffer.append()बफर है। भीख माँगने पर आपका बफर खाली है और आपके पास प्रक्रिया करने के लिए निम्नलिखित कथन है:

बयान: buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);

बफ़र: empty

पहले आपके पास buffer.append("2+3 = ")जो दिए गए स्ट्रिंग को सीधे बफर में डालता है और बन जाता है buffer। अब आपका राज्य इस तरह दिखता है:

बयान: buffer.append(cout).append(2 + 3).append(endl);

बफ़र: 2+3 = 

उसके बाद आप अपने कथन का विश्लेषण करना जारी रखते हैं और आप भर में आते हैं cout बफर के अंत तक अपील करने के तर्क के रूप में । coutके रूप में व्यवहार किया जाता है 1, ताकि आप जोड़ देंगे, 1अपने बफर के अंत तक। अब आप इस अवस्था में हैं:

बयान: buffer.append(2 + 3).append(endl);

बफ़र: 2+3 = 1

अगली बात आपके पास बफर में है 2 + 3 और चूंकि आउटपुट ऑपरेटर की तुलना में अधिक पूर्वता है, आप पहले इन दो नंबरों को जोड़ देंगे और फिर आप परिणाम को बफर में डाल देंगे। उसके बाद आपको मिलता है:

बयान: buffer.append(endl);

बफ़र: 2+3 = 15

अंत में आप का मान जोड़ते हैं endl बफर के अंत में और आपके पास है:

बयान:

बफ़र: 2+3 = 15\n

इस प्रक्रिया के बाद बफर से वर्ण मानक आउटपुट के लिए एक-एक करके बफर से प्रिंट होते हैं। तो आपके कोड का परिणाम है 2+3 = 15। आप इस पर नज़र डालें तो आप अतिरिक्त मिल 1से coutआप प्रिंट करने की कोशिश। << coutअपने बयान से हटाने से आपको वांछित आउटपुट मिलेगा।


6
हालांकि यह सब सच है (और खूबसूरती से स्वरूपित), मुझे लगता है कि यह सवाल भीख माँग रहा है। मेरा मानना है कि प्रश्न करने पर निर्भर करता "क्यों cout << coutउपज 1पहली जगह में?" , और आपने सिर्फ यह दावा किया है कि यह सम्मिलन ऑपरेटर के बारे में चर्चा के बीच में है।
बेकार

1
सुंदर प्रारूपण के लिए हालांकि +1। यह देखते हुए कि यह आपका पहला उत्तर है, यह अच्छा है कि आप मदद करने की कोशिश कर रहे हैं :)
gldraphael
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.