web-dev-qa-db-ja.com

<cmath>はC ++ 14 / C ++ 11の<math.h>でisnanを非表示にしますか?

<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>も含め、isnanstd::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がなくなったのですか?
  • 戻って古いコードを変更して新しい標準でコンパイルする必要なしに、これを修正する方法はありますか?
21
Steve Lorimer

主に Jonathan Wakelyの優れたブログ投稿 から、関連するポイントを簡単に要約します。

  • glibc <2.23の_math.h_は、C99/C++ 11バージョン(int isnan(double);)と互換性のない廃止されたX/Open bool isnan(double);を宣言します。
  • glibc 2.23の_math.h_は、C++ 11以降でisnan関数を宣言しないことにより、これを修正します。
  • それらのすべてはまだisnanマクロを定義します。 _#include <cmath>_は、C++標準で要求されているようにそのマクロを削除します。
  • GCC6のlibstdc ++は、グローバル名前空間でbool isnan(double);を宣言する独自の特別な_math.h_ヘッダーを提供し(libc _math.h_が廃止された署名を宣言しない限り)、必要に応じてマクロを削除します。標準。
  • GCC 6より前は、_#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
_
19
T.C.

GCCの<cmath>の内部を見ると、次のようになっています。

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

そのため、順序は重要ではありません。#include <cmath>の場合は常に、<math.h>が自動的に含まれ、その内容が(部分的に)削除されます。

#ifndef _MATH_Hのため、再度含めることは効果がありません。


さて、標準はこの振る舞いについて何と言っているのでしょうか?

[depr.c.headers]

...それぞれが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 ++(一部のバージョン)のバグになります。

8
rustyx

math.h内の関数の多くは実際にはマクロです。これは慣用的なc ++ではないため、ヘッダーcmathには次のコードが含まれています。

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

そして、それらすべての未定義のマクロをnamespace stdの関数として実装します。これは少なくともgcc6.1.1には当てはまります。そのため、コンパイラはisnanを見つけることができません。

3
sv90