GCCのstd::isnan()に対する警告の謎
C++/11のstd::isnan()を利用したとき、gccでわけのわからない挙動があったのでメモ書き。
動かないわけではないので無視もできたが、原因がわからず気持ち悪かったので深追いし、えらい時間を喰った。
現象
実装中、以下の現象が発生した。
環境
CentOS7上のgcc7 (DevToolkit 7)で発生。
$ gcc --version
$ gcc (GCC) 7.3.1 20180303 (Red Hat 7.3.1-5)
コード
以下コードをコンパイルすると、型変換に関するコンパイラ警告が発生する。
# include <cmath>
int main() {
double x = 3.1;
// warning: narrowing conversion of ‘isnan(x)’ from ‘int’ to ‘bool’ inside { } [-Wnarrowing]
const std::vector<bool> a = {
std::isnan(x),
};
}
g++ -std=c++14 -Wall -Wextra -Wno-unused-variable -Wconversion test.cpp
std::isnan()の戻り値はboolのはず。なんでや!
理由
cstdlib内のisnan()の定義が、C++/11仕様と、XOPENという古い拡張仕様の2つあることが原因の模様。
Prior to GCC 6 the header declared bool std::isnan(double) and bool std::isinf(double) functions for C++11 and later, and the header (which came from the C library) declared the X/Open int ::isnan(double) and int ::isinf(double) functions. If a program added a using namespace std; to the global namespace then the two sets of functions would clash, and the program would fail to compile (but only in C++11 mode!)
~
GCC 6 now detects when the obsolete functions are present in the C header and uses them, avoiding the clash (but meaning that the function might return int instead of bool as C++11 requires)
上記環境では、問題のコードは以下のように前処理されていた(isnanに関係ない部分は除去済)。
extern "C" {
extern int isnan(double __value) throw() __attribute__((__const__));
}
extern "C++" {
namespace std __attribute__((__visibility__("default"))) {
constexpr bool isnan(float __x) { return __builtin_isnan(__x); }
using ::isnan;
constexpr bool isnan(long double __x) { return __builtin_isnan(__x); }
} // namespace std__attribute__((__visibility__("default")))
}
int main() {
const std::vector<bool> y = { std::isnan(0.5), };
}
extern "C"{}
内(グローバル名前空間)で定義されたisnan()
が、usingでstd内に取り込まれている。
よって、引数がdoubleのstd::isnan()
だけはintを返す挙動になっていた、ということの模様。
その他
以下だと警告が出ない。なんでや!
double x = 3.1;
// vectorではなく配列のイニシャライザに指定 (組み込み配列と挙動が違うのはまあ納得)
const bool b[] = {
std::isnan(x),
};
// 変数ではなくリテラルをそのまま指定 (これで警告が消えるわけがわからない…)
const std::vector<bool> a = {
std::isnan(3.1),
};
C++のすごい人がレスしてくれることを願って投稿。