Search code examples
c++boostc++11template-specializationtr1

enable_if method specialization


template<typename T>
struct A
{
    A<T> operator%( const T& x);
};

template<typename T>
A<T> A<T>::operator%( const T& x ) { ... }

How can I use enable_if to make the following specialization happen for any floating point type (is_floating_point)?

template<>
A<float> A<float>::operator%( const float& x ) { ... }

EDIT: Here's an answer I came up which is different from the ones posted below...

template<typename T>
struct A
{
    T x;

    A( const T& _x ) : x(_x) {}

    template<typename Q>
    typename std::enable_if<std::is_same<Q, T>::value && std::is_floating_point<Q>::value, A<T> >::type operator% ( const Q& right ) const
    {
        return A<T>(fmod(x, right));
    }

    template<typename Q>
    typename std::enable_if<std::is_convertible<Q, T>::value && !std::is_floating_point<Q>::value, A<T> >::type operator% ( const Q& right ) const
    {
        return A<T>(x%right);
    }
};

Like the below posters say, using enable_if may not be ideal for this problem (it's very difficult to read)


Solution

  • Use overloading instead of explicit specialization when you want to refine the behavior for a more specific parameter type. It's easier to use (less surprises) and more powerful

    template<typename T>
    struct A
    {
        A<T> operator%( const T& x) { 
          return opModIml(x, std::is_floating_point<T>()); 
        }
    
        A<T> opModImpl(T const& x, std::false_type) { /* ... */ }
        A<T> opModImpl(T const& x, std::true_type) { /* ... */ }
    };
    

    An example that uses SFINAE (enable_if) as you seem to be curious

    template<typename T>
    struct A
    {
        A<T> operator%( const T& x) { 
          return opModIml(x); 
        }
    
        template<typename U, 
                 typename = typename 
                   std::enable_if<!std::is_floating_point<U>::value>::type>
        A<T> opModImpl(U const& x) { /* ... */ }
    
        template<typename U, 
                 typename = typename 
                   std::enable_if<std::is_floating_point<U>::value>::type>
        A<T> opModImpl(U const& x) { /* ... */ }
    };
    

    Way more ugly of course. There's no reason to use enable_if here, I think. It's overkill.