2016-08-24 11 views
16

मैं यहाँ जो isnan<math.h> से उपयोग करता है एक छोटे से परीक्षण से ऐप्स है:<cmath> सी ++ 14/सी ++ 11 में <math.h> में इज़न छुपाता है?

#include <iostream> 
#include <math.h> 

int main() 
{ 
    double d = NAN; 

    std::cout << isnan(d) << '\n'; 

    return 0; 
} 

बिल्ड और 3 अलग अलग मानकों के अंतर्गत चलाएँ:

$ g++ -std=c++98 main.cpp; ./a.out 
1 

$ g++ -std=c++11 main.cpp; ./a.out 
1 

$ g++ -std=c++14 main.cpp; ./a.out 
1 

अब हम भी <cmath> शामिल हैं, और isnan और std::isnan दोनों के साथ परीक्षण:

#include <iostream> 
#include <cmath> 
#include <math.h> 

int main() 
{ 
    double d = NAN; 

    std::cout << std::isnan(d) << '\n'; 
    std::cout << isnan(d) << '\n'; 

    return 0; 
} 

बिल्ड और चलाएँ:

सी ++ 98 काम करता है

$ g++ -std=c++98 main.cpp; ./a.out 
1 
1 

सी ++ 11 और सी ++ 14 नहीं है , isnan नहीं मिला है।

$ g++ -std=c++11 main.cpp 
main.cpp: In function ‘int main()’: 
main.cpp:10:25: error: ‘isnan’ was not declared in this scope 
    std::cout << isnan(d) << '\n'; 
         ^
main.cpp:10:25: note: suggested alternative: 
In file included from main.cpp:3:0: 
/usr/include/c++/5/cmath:641:5: note: ‘std::isnan’ 
    isnan(_Tp __x) 
    ^

$ g++ -std=c++14 main.cpp 
main.cpp: In function ‘int main()’: 
main.cpp:10:25: error: ‘isnan’ was not declared in this scope 
    std::cout << isnan(d) << '\n'; 
         ^
main.cpp:10:25: note: suggested alternative: 
In file included from main.cpp:3:0: 
/usr/include/c++/5/cmath:641:5: note: ‘std::isnan’ 
    isnan(_Tp __x) 
    ^

नोट शामिल किए जाने के आदेश महत्वपूर्ण नहीं है। अगर मैं <cmath><math.h> से पहले या उसके बाद, परिणाम समान है।

प्रश्न

  • क्यों isnan चला गया है?
  • नए मानक के तहत संकलित करने के लिए वापस जाने और पुराने कोड को बदलने के बिना, क्या इसे ठीक करने का कोई तरीका है?
+0

बजना libC++ का उपयोग कर रहा है, या एक ही libstdC++ कि जीसीसी है? – ildjarn

+0

@ildjarn 'ldd' कहता है कि यह वही है ('/usr/lib/x86_64-linux-gnu/libstdC++। So.6')। मैंने क्लैंग भाग को हटाने के लिए सवाल संपादित कर लिया है। –

+0

अजीब समस्या .. क्या होगा यदि आप cmath और math.h सहित तारों को स्वैप करते हैं? मेरे लिए, मैं सीएम हेडर जैसे सीएमएथ –

उत्तर

13

संक्षेप में प्रासंगिक अंक सारांश, ज्यादातर Jonathan Wakely's excellent blog post से:

  • glibc < 2.23 के math.h अप्रचलित X/ओपन int isnan(double); कि C99/C++ 11 संस्करण (bool isnan(double);) के साथ असंगत है की घोषणा की।
  • glibc 2.23 के math.h सी ++ 11 या बाद में isnan फ़ंक्शन घोषित न करके इसे ठीक करता है।
  • उनमें से सभी अभी भी isnan मैक्रो परिभाषित करते हैं। #include <cmath> सीके ++ मानक द्वारा आवश्यक मैक्रो को nukes।
  • जीसीसी 6 के libstdC++ का अपना विशेष math.h हैडर कि वैश्विक नाम स्थान में एक bool isnan(double); वाणी (जब तक कि libc math.h अप्रचलित हस्ताक्षर वाणी) है और यह भी रूप में मानक के लिए आवश्यक मैक्रो nukes प्रदान करता है।
  • जीसीसी 6, #include <math.h> से पहले बस आपके libc से हेडर शामिल किया गया था, इसलिए मैक्रो को नियुक्त नहीं किया गया है।
  • #include <cmath> हमेशा मैक्रोज़ को नियुक्त करता है।

शुद्ध परिणाम, सी ++ 11 मोड में:

glibc < 2.23, GCC < 6: <math.h> uses the macro; <cmath> uses obsolete signature 
glibc >= 2.23, GCC < 6: <math.h> uses the macro; <cmath> results in error 
glibc < 2.23, GCC >= 6: <math.h> and <cmath> use obsolete signature 
glibc >= 2.23, GCC >= 6: <math.h> and <cmath> use standard signature 
+0

@NathanOliver आपने यह जवाब देखा? –

+0

धन्यवाद - बहुत जानकारीपूर्ण! –

+1

@SteveLorimer अब मेरे पास है। धन्यवाद टी.सी. एक और महान जवाब के लिए। – NathanOliver

3

math.h के अंदर बहुत से फ़ंक्शन वास्तव में मैक्रोज़ हैं। चूंकि यह मुहावरेदार नहीं है ग हैडर cmath कोड निम्न शामिल ++:

... 
    #undef isinf 
    #undef isnan 
    #undef isnormal 
    ... 

और औजार तो namespace std में समारोह के रूप में उन सभी अपरिभाषित मैक्रो। यह जीसीसी 6.1.1 के लिए कम से कम सच है। यही कारण है कि आपका कंपाइलर isnan नहीं ढूंढ सकता है।

+0

मैं [gdbolt] (https://godbolt.org/g/eTRVmF) पर 'gcc-6.1' पर' std :: isnan' और 'isnan' दोनों को संकलित कर सकता हूं (https://godbolt.org/g/eTRVmF) –

+0

यदि यह अनिश्चितता में समस्या थी, तो शामिल करने का आदेश कोई फर्क नहीं पड़ता, लेकिन इससे कोई फर्क नहीं पड़ता –

+2

@ एंटनमालिसहेव: ऑर्डर क्यों मायने रखता है? 'cmath' में लगभग हमेशा 'math.h' शामिल होगा, जिसमें हेडर गार्ड होगा - इसे हमेशा एक बार और केवल एक बार शामिल किया जाएगा। – ildjarn

8

आप जीसीसी से अंदर <cmath> देखें, तो यह इस है:

. . . 
#include <math.h> 
. . . 
#undef isnan 

है यही कारण है कि आदेश कोई फर्क नहीं पड़ता - जब भी आप #include <cmath>, <math.h> ऑटो शामिल है और उसकी सामग्री (आंशिक रूप से) nuked है।

इसे फिर से शामिल करने का प्रयास करने से #ifndef _MATH_H की कोई प्रभाव नहीं पड़ेगी।


अब, इस व्यवहार के बारे में मानक को क्या कहना है?

[depr.c.headers]:

... हर सी हैडर, जिनमें से प्रत्येक रूप name.h का एक नाम है, बर्ताव करता है के रूप में यदि प्रत्येक नाम इसी ग नाम से मानक पुस्तकालय नाम स्थान में रखा शीर्षलेख वैश्विक नामस्थान क्षेत्र में रखा गया है। यह अनिर्दिष्ट है कि क्या इन नामों पहले घोषित या नाम स्थान std का नाम स्थान गुंजाइश ([basic.scope.namespace]) में परिभाषित किया और फिर स्पष्ट का उपयोग कर-घोषणाओं ([नाम स्थान से वैश्विक नामस्थान दायरे में इंजेक्ट किया जाता कर रहे हैं .udecl])।

[उदाहरण: शीर्षलेख <cstdlib> आश्वासन देता है कि इसकी घोषणा और नामस्थान std के भीतर परिभाषाएं प्रदान करती हैं। यह वैश्विक नामस्थान के भीतर इन नाम भी प्रदान करता है। हेडर <stdlib.h> आश्वासन दिया गया वैश्विक मानक नामस्थान के भीतर समान घोषणाएं और परिभाषाएं प्रदान करता है, जितना सी मानक में उतना ही है। यह इन नाम नामस्थान std के भीतर भी प्रदान कर सकता है। - अंत उदाहरण]

तो यह ठीक है कि <cmath> ग्लोबल नेम स्पेस में isnan प्रदान नहीं करता है।

लेकिन यह एक ग्रे क्षेत्र है, जब दोनों एक संकलन इकाई में शामिल किए गए हैं क्या होना चाहिए, हालांकि एक बहस कर सकते कि ऊपर बयान कि दोनों संस्करणों interoperate चाहिए, जिस स्थिति में यह जीसीसी में एक बग होगा तात्पर्य/libstdC++ (कुछ संस्करण)।

संबंधित मुद्दे