Home > Enterprise >  Why enable_if_t needs to have datatype identifier and a default value?
Why enable_if_t needs to have datatype identifier and a default value?

Time:03-03

I am unable to understand how the 2 commented code lines in below snippet are different than the lines just ahead of them? Is there an easy way to understand the meaning of the commented lines vs the meaning of lines just ahead of them? I am unable to speak in my mind as how to read the commented line and the line next to it. Could anyone please explain? And don't point me to the documentation please. I have spent time there and still its not clear to me else i wouldn't be posting this question here.

#include <iostream>
#include <type_traits>

//template<class T, std::enable_if_t<std::is_integral_v<T>, bool>>
template<class T, std::enable_if_t<std::is_integral_v<T>, bool> K = true>
void fun(T value)
{
    std::cout << "\n In Integral version";
}

//template<class T, std::enable_if_t<std::is_floating_point_v<T>, bool>>
template<class T, std::enable_if_t<std::is_floating_point_v<T>, bool> K = true>
void fun(T value)
{
    std::cout << "\n In Floating point version";
}

int main()
{    
    fun(4);
    fun(4.4);
}

following errors are shown when i use commented code and i dont know what they mean or how the above code resolves them.

Error(s):
2044199993/source.cpp: In function ‘int main()’:
2044199993/source.cpp:22:10: error: no matching function for call to ‘fun(int)’
     fun(4);
          ^
2044199993/source.cpp:8:6: note: candidate: template<class T, typename std::enable_if<is_integral_v<T>, bool>::type <anonymous> > void fun(T)
 void fun(T value)
      ^~~
2044199993/source.cpp:8:6: note:   template argument deduction/substitution failed:
2044199993/source.cpp:22:10: note:   couldn't deduce template parameter ‘<anonymous>’
     fun(4);
          ^
2044199993/source.cpp:15:6: note: candidate: template<class T, typename std::enable_if<is_floating_point_v<T>, bool>::type <anonymous> > void fun(T)
 void fun(T value)
      ^~~
2044199993/source.cpp:15:6: note:   template argument deduction/substitution failed:
2044199993/source.cpp:22:10: note:   couldn't deduce template parameter ‘<anonymous>’
     fun(4);
          ^
2044199993/source.cpp:23:12: error: no matching function for call to ‘fun(double)’
     fun(4.4);
            ^
2044199993/source.cpp:8:6: note: candidate: template<class T, typename std::enable_if<is_integral_v<T>, bool>::type <anonymous> > void fun(T)
 void fun(T value)
      ^~~
2044199993/source.cpp:8:6: note:   template argument deduction/substitution failed:
2044199993/source.cpp:23:12: note:   couldn't deduce template parameter ‘<anonymous>’
     fun(4.4);
            ^
2044199993/source.cpp:15:6: note: candidate: template<class T, typename std::enable_if<is_floating_point_v<T>, bool>::type <anonymous> > void fun(T)
 void fun(T value)
      ^~~
2044199993/source.cpp:15:6: note:   template argument deduction/substitution failed:
2044199993/source.cpp:23:12: note:   couldn't deduce template parameter ‘<anonymous>’
     fun(4.4);
            ^

CodePudding user response:

The following in itself is completely fine:

template<class T, std::enable_if_t<std::is_integral_v<T>, bool>>
void fun(T value)
{
    std::cout << "\n In Integral version";
}

template<class T, std::enable_if_t<std::is_floating_point_v<T>, bool>>
void fun(T value)
{
    std::cout << "\n In Floating point version";
}

Its templates with two template arguments. The issue arises when you want to call them. If you try to call them like you do in main:

int main()
{    
    fun(4);
    fun(4.4);
}

You will get an error along the line of:

<source>:18:5: error: no matching function for call to 'fun'
    fun(4);
    ^~~
<source>:5:6: note: candidate template ignored: couldn't infer template argument ''
void fun(T value)
     ^
<source>:11:6: note: candidate template ignored: couldn't infer template argument ''
void fun(T value)
     ^
<source>:19:5: error: no matching function for call to 'fun'
    fun(4.4);
    ^~~
<source>:5:6: note: candidate template ignored: couldn't infer template argument ''
void fun(T value)
     ^
<source>:11:6: note: candidate template ignored: couldn't infer template argument ''
void fun(T value)
     ^

The templates have 2 template arguments. One is T the other is either a bool or a substitution failure. The second argument cannot be deduced from the function parameters, hence the error.

Consider what you get in case of T==int. std::enable_if_t is just an alias. In your case either for bool or the alias is not defined:

template<int, bool>  // because int is integral
void fun(T value)
{
    std::cout << "\n In Integral version";
}

template<int, "substitution failure"> // because int is integral
void fun(T value)
{
    std::cout << "\n In Floating point version";
}

The second is a substitution failure, so overload resolution picks the first. And it has two template arguments.

The second template argument is not used for anything but to fail when the condition is false. You do not want the user to call

 fun<int,true>(42);

You want the caller to call it via

 fun(42);

because explicitly specifying the tempalte argument would defeat the whole purpose of SFINAE here. The way to not reqiure the caller to specify the template argument in case it cannot be deduced is to supply a default. And thats the K = true. K = false would work as well. And as you do not have to name the argument the following works as well:

template<class T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
void fun(T value)
{
    std::cout << "\n In Integral version";
}

template<class T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
void fun(T value)
{
    std::cout << "\n In Floating point version";
}
  • Related