क्या कोई घोषणा std नाम स्थान को प्रभावित कर सकती है?


96
#include <iostream>
#include <cmath>

/* Intentionally incorrect abs() which seems to override std::abs() */
int abs(int a) {
    return a > 0? -a : a;
}

int main() {
    int a = abs(-5);
    int b = std::abs(-5);
    std::cout<< a << std::endl << b << std::endl;
    return 0;
}

मुझे उम्मीद थी कि आउटपुट -5और होगा 5, लेकिन आउटपुट है -5और -5

मुझे आश्चर्य है कि यह मामला क्यों होगा?

क्या इसके उपयोग से कुछ लेना-देना है stdया क्या?


1
आपका कार्यान्वयन absगलत है।
रिचर्ड क्रिट

31
@RichardCritten यह बात है। ओपी का यह कहना कि इस टूटे हुए absको जोड़ना क्यों प्रभावित करता है std::abs()
पवित्रब्लैककट

11
दिलचस्प है, मुझे मिलता है 5और 5क्लैंग के साथ, -5और -5जीसीसी के साथ।
Rakete1111

10
Cmake एक संकलक नहीं है, बल्कि एक बिल्ड सिस्टम है। आप विभिन्न संकलक के साथ निर्माण के लिए cmake का उपयोग कर सकते हैं।
पवित्रब्लैककट

5
मैं शायद आपको केवल अपने कार्य करने की सलाह दूंगा return 0- कि लोगों को यह सोचने से परहेज होगा कि आपने अनजाने में फ़ंक्शन को गलत तरीके से लागू किया है और वांछित और वास्तविक व्यवहार को स्पष्ट किया है।
बर्नहार्ड बार्कर

जवाबों:


92

भाषा विनिर्देश वैश्विक नामस्थान में मानक कार्यों को घोषित (और परिभाषित) करके कार्यान्वयन के लिए कार्यान्वयन की अनुमति देता है और फिर उपयोग-घोषणाओं के माध्यम से उन्हें नामस्थान में लाया जाता है। यह अनिर्दिष्ट है कि क्या इस दृष्टिकोण का उपयोग किया जाता है<cmath>std

20.5.1.2 हेडर्स
4 [...] सी ++ मानक पुस्तकालय में, हालांकि, घोषणाएं (सी में मैक्रोज़ के रूप में परिभाषित किए गए नामों को छोड़कर) नाम स्थान के नाम स्थान (6.3.6) के भीतर हैं std। यह अनिर्दिष्ट है कि क्या ये नाम (क्लॉज़ 21 में 33 और एनेक्स डी के माध्यम से जोड़े गए किसी भी अधिभार सहित) को पहले वैश्विक नाम स्थान के दायरे में घोषित किया गया है और फिर stdस्पष्ट उपयोग-घोषणाओं (10.3.3) द्वारा नामस्थान में अंतःक्षिप्त किया गया है।

जाहिरा तौर पर, आप इस दृष्टिकोण (जैसे जीसीसी) का पालन करने का फैसला करने वाले कार्यान्वयन में से एक के साथ काम कर रहे हैं। यानी आपका कार्यान्वयन प्रदान करता है ::abs, जबकि std::absबस "संदर्भित" करता है ::abs

इस मामले में एक सवाल यह है कि मानक के अलावा ::absआप अपनी खुद की घोषणा करने में सक्षम ::absक्यों थे, अर्थात कोई एकाधिक परिभाषा त्रुटि क्यों नहीं है। यह कुछ कार्यान्वयनों (जैसे जीसीसी) द्वारा प्रदान की गई एक अन्य विशेषता के कारण हो सकता है: वे मानक कार्यों को तथाकथित कमजोर प्रतीकों के रूप में घोषित करते हैं , इस प्रकार आप उन्हें अपनी परिभाषाओं के साथ "प्रतिस्थापित" करने की अनुमति देते हैं।

ये दो कारक मिलकर आपके द्वारा बनाए गए प्रभाव का निर्माण करते हैं: कमजोर-प्रतीक प्रतिस्थापन के ::absपरिणामस्वरूप भी प्रतिस्थापन होता है std::abs। भाषा मानक के साथ यह कितनी अच्छी तरह से सहमत है एक अलग कहानी है ... किसी भी मामले में, इस व्यवहार पर भरोसा न करें - यह भाषा की गारंटी नहीं है।

जीसीसी में इस व्यवहार को निम्नलिखित न्यूनतर उदाहरण द्वारा पुन: प्रस्तुत किया जा सकता है। एक स्रोत फ़ाइल

#include <iostream>

void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }

एक अन्य स्रोत फ़ाइल

#include <iostream>

void foo();
namespace N { using ::foo; }

void foo() { std::cout << "Goodbye!" << std::endl; }

int main()
{
  foo();
  N::foo();
}

इस मामले में आप यह भी देखेंगे कि दूसरी स्रोत फ़ाइल में ::foo( "Goodbye!") की नई परिभाषा के व्यवहार को भी प्रभावित करता है N::foo। दोनों कॉल आउटपुट करेंगे "Goodbye!"। और यदि आप ::fooदूसरी स्रोत फ़ाइल से परिभाषा को हटाते हैं , तो दोनों कॉल "मूल" ::fooऔर आउटपुट की परिभाषा को भेज देंगे "Hello!"


उपरोक्त 20.5.1.2/4 द्वारा दी गई अनुमति के कार्यान्वयन को सरल बनाने के लिए है <cmath>। कार्यान्वयन को केवल सी-शैली में शामिल करने की अनुमति है <math.h>, फिर कार्यों को फिर से stdजोड़ने और कुछ सी ++ - विशिष्ट परिवर्धन और ट्वीक्स जोड़ें। यदि उपरोक्त स्पष्टीकरण मुद्दे के आंतरिक यांत्रिकी का ठीक से वर्णन करता है, तो इसका एक प्रमुख हिस्सा कार्यों के सी-शैली संस्करणों के लिए कमजोर प्रतीकों की पुनरावृत्ति पर निर्भर करता है।

ध्यान दें कि यदि हम उपरोक्त कार्यक्रम में केवल विश्व स्तर पर प्रतिस्थापित intकरते हैं double, तो कोड (GCC के तहत) "अपेक्षित रूप से व्यवहार करेगा" - यह आउटपुट होगा -5 5। ऐसा इसलिए होता है क्योंकि C मानक लाइब्रेरी में abs(double)फ़ंक्शन नहीं होता है। अपनी खुद की घोषणा करके abs(double), हम कुछ भी प्रतिस्थापित नहीं करते हैं।

लेकिन अगर से स्विच करने के बाद intसाथ doubleहम भी से स्विच absकरने के लिए fabs, मूल अजीब व्यवहार अपनी पूरी महिमा (उत्पादन में फिर से दिखाई देगा -5 -5)।

यह उपरोक्त स्पष्टीकरण के अनुरूप है।


जैसा कि मैं cmath के स्रोत में देख सकता हूं कि ऐसा कुछ नहीं है using ::abs;, using ::asin;इसलिए आप घोषणा को ओवरराइड कर सकते हैं, उल्लेख करने के लिए एक और बिंदु यह है कि एसटीडी नेमस्पेस फ़ंक्शन में परिभाषित इंट के लिए घोषित नहीं हैं, बल्कि डबल , फ्लोट के लिए
टेक_केयर और

2
मानक की दृष्टि से, व्यवहार प्रति अपरिपक्व है [extern.names] / 4
xskxzr

लेकिन जब मैंने #include<cmath>अपने कोड को हटा दिया , तो मुझे वही जवाब मिला। `
पीटर

@Peter लेकिन फिर आप कहाँ से std कर रहे हैं :: abs? - यह एक और शामिल के माध्यम से शामिल किया जा सकता है, जिस बिंदु पर आप इस स्पष्टीकरण पर वापस आते हैं। (यह संकलक के लिए कोई फर्क नहीं पड़ता है अगर एक हेडर प्रत्यक्ष या अप्रत्यक्ष रूप से शामिल है।)
आरएम

@ पेटर: के रूप में अच्छी तरह से absघोषित किया जा सकता है <cstdlib>, जिसे अंतर्निहित रूप से शामिल किया जा सकता है<iostream> । अपने खुद को हटाने की कोशिश करें absऔर देखें कि क्या यह अभी भी संकलित है।
चींटी

13

आपका कोड अपरिभाषित व्यवहार का कारण बनता है।

C ++ 17 [extern.names] / 4:

बाहरी लिंकेज के साथ घोषित सी मानक पुस्तकालय से प्रत्येक फ़ंक्शन हस्ताक्षर बाहरी "सी" और बाहरी "सी ++" लिंकेज, या ग्लोबल नेमस्पेस में नेमस्पेस गुंजाइश के नाम के रूप में फ़ंक्शन हस्ताक्षर के रूप में उपयोग के लिए लागू करने के लिए आरक्षित है।

तो आप मानक C लाइब्रेरी फ़ंक्शन के समान प्रोटोटाइप के साथ फ़ंक्शन नहीं कर सकते int abs(int);। भले ही आप वास्तव में उन हेडर को शामिल करें या चाहे उन हेडर ने सी लाइब्रेरी के नामों को वैश्विक नामस्थान में रखा हो।

हालाँकि, absयदि आप अलग-अलग पैरामीटर प्रकार प्रदान करते हैं , तो इसे ओवरलोड करने की अनुमति होगी ।


1
"या ग्लोबल नेमस्पेस में नेमस्पेस स्कोप के नाम के रूप में", इसलिए इसे ग्लोबल नेमस्पेस में ओवरलोड नहीं किया जा सकता है।
xskxzr

@xskxzr मैं आपके द्वारा उद्धृत पाठ की व्याख्या के बारे में निश्चित नहीं हूं; यदि यह अर्थ लिया जाता है कि उपयोगकर्ता वैश्विक नामस्थान में उस नाम का कुछ भी घोषित नहीं कर सकता है, तो मेरे द्वारा उद्धृत पाठ का पिछला भाग बेमानी होगा, जैसा कि अधिकांश [extern.names] / 3 है। जो मुझे यह सोचने के लिए प्रेरित करता है कि यहां कुछ और इरादा था।
MM
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.