Search code examples
c++templatessfinaeenable-if

Why type deduction of function parameters are prioritized over the types from template?


Lets have a look at the simple example of enable_if usages

template <bool, typename T = void>
struct enable_if
{
};

template <typename T>
struct enable_if<true, T>
{
    typedef T type;
};
  1. Here it is expected that y would be int, since T is our "enabled" type.
template <typename T, typename Y = typename std::enable_if<std::is_integral<T>::value, T>::type>
void do_stuff(T t)
{
    (void)t;
    Y y;
    std::cout << typeid(y).name() << std::endl;
}
do_stuff(15); // int
  1. But here things go somewhat non-explicit for me, and y will be deduced to whatever the type of the actual argument is going to be
template <typename T, typename Y = typename std::enable_if<std::is_integral<T>::value, T>::type>
void do_stuff(T t, Y y)
{
    (void)t;
    std::cout << "do_stuff integral\n";
    std::cout << typeid(y).name() << std::endl;
}

do_stuff(15, 14.3);  // double

So my question is:

What could be the rules that forces compiler to prioritize deduction of y from the actual argument passed and ignore the enabled type from enable_if ?


Solution

  • Default template argument is used only when template argument isn't specified explicitly and template parameter can't be deduced. For this case, Y could be deduced from 14.3 as double, then default argument won't be used. Similarly, in the 1st sample you can also bypass the check of std::enable_if by specifying template argument.

    do_stuff<int, double>(15); // double
    

    BTW you can add non-type template parameter using std::enable_if in type declaration to make check taking effect all the time.

    template <typename T, typename Y, typename std::enable_if<std::is_integral<T>::value, T>::type* = nullptr>
    void do_stuff(T t, Y y)