Why is isnan ambiguous and how to avoid it?

vitaut picture vitaut · Nov 18, 2015 · Viewed 13.1k times · Source

Since isnan can be either a macro (in C++98) or a function defined in namespace std (in C++11), an obvious (and possibly naive) way to write the code that works in both cases is illustrated by this simple example

#include <cmath>

int main() {
  double x = 0;
  using namespace std;
  isnan(x);
}

However, compiling it gives errors both in GCC (with -std=c++11) and 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)
   ^

Why is this ambiguous in C++11 and how to make it work with both C++98 and C++11 preferably without too much conditional compilation?

Answer

Shafik Yaghmour picture Shafik Yaghmour · Nov 18, 2015

This is a libstdc++ bug documented in the bug report std functions conflicts with C functions when building with c++0x support (and using namespace std) with a reproducing sample very similar to the OP's:

#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;
}

and one of the comments says:

I don't think that's the problem, because libstdc++ has always declared the names in the global namespace even though it wasn't valid in C++03 - we haven't changed that for C++0x (all that happened is the standard was relaxed to reflect the reality of actual implementations)

This may eventually get fixed until then the solution provided from the bug report is as follows:

Qualify isnan explicitly, by calling either ::isnan or std::isnan

Using ::isnan as far as I can tell works pre C++11 and in C++11.

Of course this is a libstdc++ specific solution, it looks valid in libc++ as well but if you need to support a compiler where this does not work you will probably have to resort to using #if/#else.

Note, as indicated by M.M having isnan marked constexpr is non-conforming, this is a known issue as well although it does not contribute to this particular issue.

Also see related bug reports: [C++11] call of overloaded ‘isnan’ is ambiguous and Recognize builtins with bool return type. The second discusses possible libstdc++ solutions.

Update

If you want a gcc/clang solution it looks like they both support __builtin_isnan, see gcc docs on builtins for more information. Also see this glibc bug report on replacing isnan et al with the builtin.