12

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?

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
vitaut
  • 49,672
  • 25
  • 199
  • 336
  • Sure. I've added the complete compilable example – vitaut Nov 18 '15 at 01:54
  • If you still get the error by writing `(isnan)(x);` then it's nothing to do with macros – M.M Nov 18 '15 at 01:55
  • I don't get the error in g++ 4.9.2 for Windows, but do get it on godbolt which uses some sort of Linux. So perhaps it is a bug in glibc headers – M.M Nov 18 '15 at 01:56
  • 3
    also somewhat of a bug that `std::isnan(long double)` and `std::isnan(float)` are listed, as they could never be selected anyway; the ambiguity is between `::isnan(double)` and `std::isnan(double)` – M.M Nov 18 '15 at 01:58
  • @M.M Nice observations. I wonder if there is a way to distinguish between `std::isnan` and `::isnan` somehow. – vitaut Nov 18 '15 at 01:59
  • Is it the non-standard attribute that's interfering, or the ambiguity between `::isnan(double)` and `std::isnan(double)`? – John Drouhard Nov 18 '15 at 02:16
  • 1
    @JohnDrouhard good point, the real bug is that there are two different functions. The C++ Standard requires that if `::isnan` is present, then `std::isnan` must be the same function injected into namespace std. The C++ library should be using the C library's `isnan(double)` and adding only `std::isnan(long double)` and `std::isnan(float)` . – M.M Nov 18 '15 at 02:30
  • I think `constexpr` here is a bug too; C++14 [constexpr.functions]/1 says "An implementation shall not declare any standard library function signature as constexpr except for those where it is explicitly required." The standard does NOT specify that `isnan` should be `constexpr`. – M.M Nov 18 '15 at 02:33
  • @M.M cmath functions being [constexpr is a known libstdc++ issue](http://stackoverflow.com/q/27744079/1708801). – Shafik Yaghmour Nov 18 '15 at 02:35
  • @M.M there is already a bug report, I included it in my answer. – Shafik Yaghmour Nov 18 '15 at 02:35

4 Answers4

16

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.

Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • I think the quoted comment is overlooking the problem. The problem is that the headers have declared *two different functions* `::isnan(double)` and `std::isnan(double)`. The comment seems to be talking about the (non-) ambiguity between `::foo` and `std::foo` in the case where `::foo`'s name was injected into `std` by the headers (which is explicitly permitted as of C++11). – M.M Nov 18 '15 at 02:37
  • @M.M agreed, it is more complex but including all the comments would be a very long answer. – Shafik Yaghmour Nov 18 '15 at 02:38
6

After thinking about this for a while, I don't think there is a portable way to do this prior to C++11 at all. The C isnan macro was introduced in C99 but C++98 and C++03 are based on C89. So if you rely on your C++98/03 implementation to drag in a C99 header that provides isnan (which is non-conforming, by the way) you're making non-portable assumptions anyway.

Replacing the unsing directive with a using declaration then gives you the following code that is portable C++11 (also working with libstdc++'s defect) and might work for earlier implementations with both fingers crossed. (Regardless whether they provide isnan as a macro or a function in the global namespace.)

template <typename T>
bool
my_isnan(const T x)
{
#if __cplusplus >= 201103L
  using std::isnan;
#endif
  return isnan(x);
}

Wrapping this in its own function seems to make the #if acceptable.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
4

make your own:

bool isNaN(double x) { 
  return x != x;
}
DU Jiaen
  • 955
  • 6
  • 14
2

The errors indicate that you have an isnan in the global namespace, and another one in the std namespace. The "using namespace std;" causes ambiguity between those.

Not overly elegant, but the following could work for your stated requirements.

// drop 'using namespace std;'

#ifndef isnan
using std::isnan;
#endif

[ EDIT ] The above applies to the part of the original question about avoiding ambiguity between a macro isnan and the std::isnan. If there is a 3rd conflicting ::isnan in the global namespace then the following would technically cover it, but that's even uglier and more fragile.

// drop 'using namespace std;'

#ifndef isnan
#define isnan(x) std::isnan(x)
#endif

[ EDIT #2 ] In reply to the comment about "fail to compile on C++98, which doesn't have the macro defined (it's a normal function in the global namespace), nor does it have isnan(double&) in the std namespace"... Something like this might work in an ideal world.

#ifndef isnan
#if __cplusplus <= 199711L  // c++98 or older
#  define isnan(x) ::isnan(x)
#else
#  define isnan(x) std::isnan(x)
#endif
#endif

In the real world however compilers have different rules for __cplusplus which are quite inconsistent. For a more general discussion and answers I'll defer to how do I make a portable isnan/isinf function.

Community
  • 1
  • 1
dxiv
  • 16,984
  • 2
  • 27
  • 49
  • Your analysis of the problem is correct but the proposed fix won't work. Because there is no `isnan` macro in Glibc. – 5gon12eder Nov 18 '15 at 02:02
  • @5gon12eder I did not try it here, and don't have the setup to. The reply applies to the part of the original question about avoiding ambiguity between a macro isnan and the std::isnan. If there is a 3rd conflicting ::isnan in the global namespace (as it seems to be the case) then that's indeed not covered. – dxiv Nov 18 '15 at 02:08
  • Your edit would unfortunately still fail to compile on C++98, which doesn't have the macro defined (it's a normal function in the global namespace), nor does it have `isnan(double&)` in the std namespace. – John Drouhard Nov 18 '15 at 02:13
  • @dxiv the original question is about ambiguity between two functions. The mention of "macro" was incorrect speculation by OP – M.M Nov 18 '15 at 02:13
  • @JohnDrouhard you're right, and I added a note to that effect. – dxiv Nov 18 '15 at 02:35