“What happened to my SFINAE” redux: conditional template class members?

Firstly, C++11 did not carry forward boost’s disable_if. So if you’re going to transition boost code, you’ll need to use enable_if with a negated condition (or redefine your own disable_if construct).

Secondly, for SFINAE to reach in and apply to the method level, those methods must be templates themselves. Yet your tests have to be done against those templates’ parameters…so code like enable_if<is_pointer<T> will not work. You can finesse this by making some template argument (let’s say X) default to be equal to T, and then throw in a static assertion that the caller has not explicitly specialized it to something else.

This means that instead of writing:

template <typename T>
struct Foo {
    typename enable_if<is_pointer<T>::value, void>::type
    valid_if_pointer(T) const { /* ... */ }

    typename disable_if<is_pointer<T>::value, void>::type
    valid_if_not_pointer(T) const { /* ... */ }
};

…you would write:

template <typename T>
struct Foo {
    template <typename X=T>
    typename enable_if<is_pointer<X>::value, void>::type
    valid_if_pointer(T) const {
        static_assert(is_same<X,T>::value, "can't explicitly specialize");
        /* ... */
    }

    template <typename X=T>    
    typename enable_if<not is_pointer<X>::value, void>::type
    valid_if_not_pointer(T) const {
        static_assert(is_same<X,T>::value, "can't explicitly specialize");
        /* ... */
    }
};

Both are now templates and the enable_if uses the template parameter X, rather than T which is for the whole class. It’s specifically about the substitution that happens whilst creating the candidate set for overload resolution–in your initial version there’s no template substitution happening during the overload resolution.

Note that the static assert is there to preserve the intent of the original problem, and prevent someone being able to compile things like:

Foo<int>().valid_if_pointer<int*>(someInt);

Leave a Comment