1
votes

I have the following code:

#include <iostream>
#include <type_traits>

template <typename T, typename std::enable_if
                                 <std::is_convertible<int, T>::value, T>::type>
void func(T a)
{
    std::cout << a << std::endl;
}

template <typename T, typename std::enable_if
                                 <!std::is_convertible<int, T>::value, T>::type>
void func(T a)
{
    a.print();
}

class Test
{
public:
    void print()
    {
        std::cout << "Test" << std::endl;
    }
};    

int main()
{
    func(3);
    func("Test");
    return 0;
}

With this code, I expected the first call to func to print out 3 (as int is indeed convertible to int, the first specialization should be called) and the second call to func to print out Test (Test() is not convertible to int, so the second specialization should be called). However, I instead get a compiler error:

prog.cpp: In function ‘int main()’:

prog.cpp:27:8: error: no matching function for call to ‘func(int)’

prog.cpp:5:6: note: candidate: template [class T, typename std::enable_if[std::is_convertible[int, T>::value, T>::type > void func(T)

prog.cpp:5:6: note: template argument deduction/substitution failed:

prog.cpp:27:8: note: couldn't deduce template parameter ‘[anonymous>’

If, however, I change the templated functions to instead be (while leaving everything else exactly the same):

template <typename T, typename std::enable_if
                                 <std::is_convertible<int, T>::value, T>::type* =
                                  nullptr>
void func(T a)
{
    std::cout << a << std::endl;
}

template <typename T, typename std::enable_if
                                 <!std::is_convertible<int, T>::value, T>::type* =
                                  nullptr>
void func(T a)
{
    a.print();
}

then everything compiles and works as I expected. What is this extra syntax doing and why do I need it?

1

1 Answers

7
votes
template<typename T, typename std::enable_if<std::is_convertible<int, T>::value, T>::type>

if we were to remove the noise, would become

template<typename T, typename Something<T>::type>

which is declaring as its second parameter a non-type parameter, the typename here is specifying the nested type is a name of a type. See here for more information.

In the first case, the second parameter is non-type, so the function call func(3) doesn't fit the template which is expecting func<int, some_int>(3).