<math.h>
のisnan
を使用する小さなテストアプリがあります。
#include <iostream>
#include <math.h>
int main()
{
double d = NAN;
std::cout << isnan(d) << '\n';
return 0;
}
つの異なる標準の下で構築および実行:
$ 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;
}
ビルドして実行:
C++ 98は動作します
$ g++ -std=c++98 main.cpp; ./a.out 1 1
C++ 11およびC++ 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
がなくなったのですか?主に Jonathan Wakelyの優れたブログ投稿 から、関連するポイントを簡単に要約します。
math.h
_は、C99/C++ 11バージョン(int isnan(double);
)と互換性のない廃止されたX/Open bool isnan(double);
を宣言します。math.h
_は、C++ 11以降でisnan
関数を宣言しないことにより、これを修正します。isnan
マクロを定義します。 _#include <cmath>
_は、C++標準で要求されているようにそのマクロを削除します。bool isnan(double);
を宣言する独自の特別な_math.h
_ヘッダーを提供し(libc _math.h
_が廃止された署名を宣言しない限り)、必要に応じてマクロを削除します。標準。#include <math.h>
_は単にlibcからのヘッダーを含んでいたので、マクロは無意味ではありません。#include <cmath>
_は常にマクロを削除します。最終結果、C++ 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
_
GCCの<cmath>
の内部を見ると、次のようになっています。
. . .
#include <math.h>
. . .
#undef isnan
そのため、順序は重要ではありません。#include <cmath>
の場合は常に、<math.h>
が自動的に含まれ、その内容が(部分的に)削除されます。
#ifndef _MATH_H
のため、再度含めることは効果がありません。
さて、標準はこの振る舞いについて何と言っているのでしょうか?
...それぞれが
name.h
の形式の名前を持つすべてのCヘッダーは、対応するcnameによって標準ライブラリ名前空間に配置された各名前のように動作します。ヘッダーはグローバル名前空間スコープ内に配置されます。これらの名前が最初に名前空間std
の名前空間スコープ([basic.scope.namespace])内で宣言または定義され、次に明示的によってグローバル名前空間スコープに挿入されるかどうかは指定されていません。 using-declarations([namespace.udecl])。[例:ヘッダー
<cstdlib>
は、名前空間std
内で宣言と定義を確実に提供します。 mayも、グローバル名前空間内でこれらの名前を提供します。ヘッダー<stdlib.h>
は、C標準と同様に、グローバル名前空間内で同じ宣言と定義を確実に提供します。また、名前空間std
内にこれらの名前を提供する場合もあります。 —終了例]
したがって、<cmath>
がグローバル名前空間にisnan
を提供しないことは問題ありません。
しかし、bothが1つのコンパイル単位に含まれている場合に何が起こるかは灰色の領域ですが、上記のはを意味すると主張することもできます。両方のバージョンが相互運用する必要があること。この場合、GCC/libstdc ++(一部のバージョン)のバグになります。
math.h
内の関数の多くは実際にはマクロです。これは慣用的なc ++ではないため、ヘッダーcmath
には次のコードが含まれています。
...
#undef isinf
#undef isnan
#undef isnormal
...
そして、それらすべての未定義のマクロをnamespace std
の関数として実装します。これは少なくともgcc6.1.1には当てはまります。そのため、コンパイラはisnan
を見つけることができません。