SFINAE to detect static method
Asked Answered
F

2

5

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.

Fantinlatour answered 8/10, 2017 at 23:3 Comment(0)
T
5

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.

Tempi answered 8/10, 2017 at 23:9 Comment(4)
I forgot that specialization should be for the default template parameters. Thank you!Fantinlatour
just love the comma operatorClemenciaclemency
@Clemenciaclemency - so do I.Tempi
Or void_t<decltype(T::Foo)> , that's what it's for.Poindexter
L
1

As this may still interest people, let me remark that the EnableIfHasFooMethod is superfluous (if I am not mistaken). This should work just as well:

template <class T, class = void>
struct HasFooMethod: public std::false_type {};
template <class T>
struct HasFooMethod<T, std::void_t<decltype(T::Foo)>>: public std::true_type {};
Largehearted answered 20/4, 2020 at 14:56 Comment(1)
But that won't distinguish between a static function Foo() and a member variable Foo.Jereme

© 2022 - 2024 — McMap. All rights reserved.