Search code examples
c++templatesc++11enable-if

Using std::enable_if


I'm trying to specialize a function for a range of types using std::enable_if.
Here is a a simpler version of what I'm trying to accomplish.

#include <type_traits>
#include <string>

struct Ip
{};

template <typename T>
bool Equal(const std::string& s, const T& data)
{return s == data;}

template <typename T>
bool Equal(const std::string& s, const typename std::enable_if<std::is_integral<T>::value, T>::type& data)
{
    //int specific
}

template <typename T>
bool Equal(const std::string& s, const typename std::enable_if<std::is_floating_point<T>::value, T>::type& data)
{
    //Float specific
}

//Specialization
template <> bool Equal(const std::string& s, const Ip& data)
{
   //Ip specific
} 

int main()
{
    //Equal("21",21); // Do not compile
    Equal("21","42.5"); // Compile
}

but when trying to compile, the template functions with std::enable_if does not seem to participate in the resolution, and so the compiler tells me that there is no function that match my function call. I tried using std::enable_if with the return type, but no luck there either. I'm sure there is something I'm doing terribly wrong in this code, or maybe I'm trying to do this the wrong way.
I'm trying to not write every int specialization (int, short, long, long long, ...), so does anyone have a solution for this ?


Solution

  • I'd make several changes to your code, beginning with the unconstrained function template. Since you're handling arithmetic types separately, that should only be selected if T is neither integral nor floating point.

    template <typename T>
    typename std::enable_if<!std::is_integral<T>::value &&
                            !std::is_floating_point<T>::value, bool>::type 
        Equal(const std::string& s, const T& data)
    {return s == data;}
    

    Now, the way you've defined Equal for integral and floating point types causes the T to be a non-deduced context. So move the enable_if to a dummy template parameter to allow deduction (you could also use the return type, as above).

    template <typename T,
              typename std::enable_if<std::is_integral<T>::value, T>::type* = nullptr>
    bool Equal(const std::string& s, const T& data)
    {
        //int specific
        return false;
    }
    
    template <typename T,
              typename std::enable_if<std::is_floating_point<T>::value, T>::type* = nullptr>
    bool Equal(const std::string& s, const T& data)
    {
        //Float specific
        return false;
    }
    

    Finally, there's no need to specialize for handling Ip, create an overload instead.

    //Overload
    bool Equal(const std::string& s, const Ip& data)
    {
       //Ip specific
        return false;
    }
    

    Live demo