8

I was trying to come up with a hack to test if std::isnan is defined without special casing compilers in the preprocessor, and came up with the following, which I was expecting to work fine.

#include <cmath>
#include <type_traits>

namespace detail {
    using namespace std;

    struct dummy {};
    void isnan(dummy);

    //bool isnan(float); // Just adding this declaration makes it work!

    template <typename T>
    struct is_isnan_available {
        template <typename T1>
        static decltype(isnan(T1())) test(int);
        template <typename>
        static void test(...);

        enum { value = !std::is_void<decltype(test<T>(0))>::value };
    };
}

int main() {
    return detail::is_isnan_available<float>::value;
}

Turns out it doesn't detect it. I know for certain std::isnan is defined on ideone, because I tested that manually.

And when I uncomment the marked line above, it works.

What am I missing here? What explains this behaviour?

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • Curious: does is_isnan_available::value work? – Tony Delroy Jan 06 '12 at 11:00
  • @Tony: `std::isnan` is overloaded for both. And even then, there's an implicit conversion from float to double. Still, I tried and testing for double doesn't work either. – R. Martinho Fernandes Jan 06 '12 at 11:03
  • @TonyDelroy: good thought, however the standard functions are usually overloaded. Still for the heck of it, you can check that it still does not work on [ideone](http://www.ideone.com/U7EqO) – Matthieu M. Jan 06 '12 at 11:04
  • @R.MartinhoFernandes: you could use `std::declval()` to provide the value to `isnan` -- okay, maybe overkill here and completely unrelated, useful to know for class types though (without necessarily a default constructor) – Matthieu M. Jan 06 '12 at 11:08
  • @Matthieu: thanks, I knew of that, but since I was only interested in the standard floating point types I went with the readable version :) – R. Martinho Fernandes Jan 06 '12 at 11:30

2 Answers2

7

The thing is, that the using directive doesn't add members to the current namespace, so the std:: members could still be hidden by declarations in this namespace.

using std::isnan would instead behaves as if the members of the imported namespace were added to the namespace enclosing both the use-location and the imported namespace. The using declaration is a normal declaration in the namespace, so can take part in overload resolution with the declarations that follow.

However, as pointed out in the comments, that would produce an error if the function does not exist. To work around that you need to put it out of your detail:: namespace then. That should work, because the imported definition would be at the same level as the dummy overload. You can take the overload to the global namespace, or you can make an auxiliary namespace (in the global namespace) and import both.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
jpalecek
  • 47,058
  • 7
  • 102
  • 144
  • That will cause a hard error if it doesn't exist. http://www.ideone.com/44sE3. I have `using namespace` because I can't name the function if I'm expecting it to not exist. – R. Martinho Fernandes Jan 06 '12 at 11:06
  • Can you explain what the difference is, in terms of name lookup, between those two ? Intuitively I thought that `using namespace` was just a shortcut to import all symbols at once, I certainly did not expect discrepancies in term of name lookup. – Matthieu M. Jan 06 '12 at 11:06
  • Ah, that's it! I didn't know I would hide the declaration from `std` with that. Thanks. – R. Martinho Fernandes Jan 06 '12 at 11:19
  • Ah thanks for the distinction. I didn't know that the `using namespace` injected symbols were not "really" injected and thus still subject to name hiding. – Matthieu M. Jan 06 '12 at 13:10
1

I solved this problem for the set of POSIX thread-safe APIs which supersede non-thread-safe standard functions: C++11 alternative to localtime_r . This code detects whether an API is defined in the global namespace, and if it does not exist, selects a custom workaround.

Community
  • 1
  • 1
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421