由于isnan
可以是宏(在C ++ 98中)或在命名空间std
中定义的函数(在C ++ 11中),因此可以使用一种明显的(可能是天真的)方式来编写这两个案例中的代码由这个简单的例子说明
#include <cmath>
int main() {
double x = 0;
using namespace std;
isnan(x);
}
但是,编译它会在GCC(使用-std = c ++ 11)和Clang中产生错误:
test.cc: In function ‘int main()’:
test.cc:6:10: error: call of overloaded ‘isnan(double&)’ is ambiguous
isnan(x);
^
test.cc:6:10: note: candidates are:
In file included from /usr/include/features.h:374:0,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/os_defines.h:39,
from /usr/include/x86_64-linux-gnu/c++/4.8/bits/c++config.h:426,
from /usr/include/c++/4.8/cmath:41,
from test.cc:1:
/usr/include/x86_64-linux-gnu/bits/mathcalls.h:234:1: note: int isnan(double)
__MATHDECL_1 (int,isnan,, (_Mdouble_ __value)) __attribute__ ((__const__));
^
In file included from test.cc:1:0:
/usr/include/c++/4.8/cmath:626:3: note: constexpr bool std::isnan(long double)
isnan(long double __x)
^
/usr/include/c++/4.8/cmath:622:3: note: constexpr bool std::isnan(double)
isnan(double __x)
^
/usr/include/c++/4.8/cmath:618:3: note: constexpr bool std::isnan(float)
isnan(float __x)
^
为什么C ++ 11中的这个含糊不清以及如何使它适用于C ++ 98和C ++ 11,最好没有太多的条件编译?
答案 0 :(得分:11)
这是错误报告std functions conflicts with C functions when building with c++0x support (and using namespace std)中记录的libstdc++
错误,其重现示例与OP非常相似:
#include <stdlib.h>
#include <cmath>
#include <stdio.h>
using namespace std;
int main(int argc, char** argv)
{
double number = 0;
if (isnan(number))
{
printf("Nan\n");
}
return 0;
}
其中一条评论说:
我不认为这是问题,因为libstdc ++总是在全局命名空间中声明名称,即使它在C ++ 03中无效 - 我们没有为C ++ 0x更改它(所有这些发生的是放松的标准,以反映实际实施的现实)
这可能最终得到解决,直到那时bug报告提供的解决方案如下:
通过调用:: isnan或std :: isnan
明确限定isnan
据我所知,使用::isnan
作品pre C++11和in C++11。
当然这是一个libstdc++
特定的解决方案,它在libc++
中看起来也是有效的,但如果你需要支持一个不起作用的编译器,你可能不得不求助于{{ 1}}。
注意,如M.M所示,#if/#else
标记为constexpr是不符合的,这是known issue as well,尽管它不会对此特定问题做出贡献。
另请参阅相关错误报告:[C++11] call of overloaded ‘isnan’ is ambiguous和Recognize builtins with bool return type。第二部分讨论了可能的isnan
解决方案。
更新
如果你想要一个gcc / clang解决方案,它们看起来都支持libstdc++
,请参阅gcc docs on builtins了解更多信息。关于用内置函数替换isnan等人,请参阅此glibc bug report。
答案 1 :(得分:4)
在考虑了一段时间之后,我根本不认为在C ++ 11之前有一种可移植的方法。 C isnan
宏在C99中引入,但C ++ 98和C ++ 03基于C89。因此,如果您依赖于C ++ 98/03实现来拖动提供isnan
的C99标头(顺便说一句,这是不合格的),那么您无论如何都要进行非便携式假设。 / p>
用unsing
声明替换using
指令然后为您提供以下可移植的C ++ 11代码(也使用libstdc ++的缺陷)并且可能适用于两个手指交叉的早期实施。 (无论他们是否在全局isnan
中提供namespace
作为宏或函数。)
template <typename T>
bool
my_isnan(const T x)
{
#if __cplusplus >= 201103L
using std::isnan;
#endif
return isnan(x);
}
将其包含在自己的函数中似乎使#if
可以接受。
答案 2 :(得分:3)
制作自己的:
bool isNaN(double x) {
return x != x;
}
答案 3 :(得分:2)
错误表明您在全局命名空间中有一个isnan,在std命名空间中有另一个。 &#34;使用命名空间std;&#34;引起歧义。
并不过分优雅,但以下内容可以满足您的要求。
// drop 'using namespace std;'
#ifndef isnan
using std::isnan;
#endif
[编辑] 以上内容适用于原始问题的一部分,即避免宏isnan和std :: isnan之间的歧义。如果全局命名空间中存在第三个冲突的:: isnan,那么以下内容在技术上会覆盖它,但这更加丑陋且更脆弱。
// drop 'using namespace std;'
#ifndef isnan
#define isnan(x) std::isnan(x)
#endif
[编辑#2] 回复关于&#34; 的评论无法在C ++ 98上编译,而C ++ 98没有定义宏(&# 39;在全局命名空间中是正常函数),在std命名空间中也没有isnan(double&amp;)&#34; ......这样的东西可能在理想世界中起作用。
#ifndef isnan
#if __cplusplus <= 199711L // c++98 or older
# define isnan(x) ::isnan(x)
#else
# define isnan(x) std::isnan(x)
#endif
#endif
在现实世界中,编译器对__cplusplus有不同的规则,这些规则非常不一致。对于更一般的讨论和答案,我将遵循how do I make a portable isnan/isinf function。