为什么isnan含糊不清以及如何避免它?

时间:2015-11-18 01:42:20

标签: c++ c++11 gcc compiler-errors cmath

由于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,最好没有太多的条件编译?

4 个答案:

答案 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++11in C++11

当然这是一个libstdc++特定的解决方案,它在libc++中看起来也是有效的,但如果你需要支持一个不起作用的编译器,你可能不得不求助于{{ 1}}。

注意,如M.M所示,#if/#else标记为constexpr是不符合的,这是known issue as well,尽管它不会对此特定问题做出贡献。

另请参阅相关错误报告:[C++11] call of overloaded ‘isnan’ is ambiguousRecognize 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