Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SFINAE to test a free function from another namespace

Tags:

c++

sfinae

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?

like image 963
R. Martinho Fernandes Avatar asked Jan 06 '12 10:01

R. Martinho Fernandes


2 Answers

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.

like image 165
jpalecek Avatar answered Nov 18 '22 09:11

jpalecek


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.

like image 1
Potatoswatter Avatar answered Nov 18 '22 11:11

Potatoswatter