Search code examples
c++c++11templatestemplate-specializationsfinae

SFINAE to detect static method


I'm trying to implement a mechanism to detect whether provided class contains some static method or not. It's quite simple code but I cannot understand why decltype() doesn't work as expected for specialization of EnableIfHasFooMethod class:

#include <iostream>

struct A {
    static int Foo() { return 0; }
};

template <class T, class = void>
struct EnableIfHasFooMethod {};

template <class T>
struct EnableIfHasFooMethod<T, decltype(T::Foo)> {
    typedef void type;
};

template <class T, class = void>
struct HasFooMethod {
    static const bool value = false;
};

template <class T>
struct HasFooMethod<T, typename EnableIfHasFooMethod<T>::type> {
    static const bool value = true;
};

int main() {
    std::cout << HasFooMethod<A>::value << std::endl;
    return 0;
}

Output is 0, but should be 1.


Solution

  • You forget to add void()

    template <class T>
    struct EnableIfHasFooMethod<T, decltype(T::Foo, void())> { /* ... */ };
    // ...........................................^^^^^^^^
    

    You need to match the second type (void) in

    // ........................vvvv
    template <class T, class = void>
    struct EnableIfHasFooMethod {};
    

    so your decltype() must return void iff (if and only if) there is a Foo() member in T.

    You can't write

    decltype( T::Foo )
    

    because, in this case, decltype() return the type of the member Foo (if present) that can't be void.

    You can't write

    decltype( void() )
    

    because, in this case, decltype() return ever void, but you want it iff there is a Foo member in T

    So the solution is

    decltype( T::Foo , void() )
    

    so SFINAE can work, failing the substitution, if there isn't a Foo member and returning void if there is Foo.