I'm trying to understand universal references and std::enable_if better, but I'm a little stuck as to what's going on here in my code.
First off, I've noticed people seem to use std::enable_if in two different ways:
template<typename T, std::enable_if<condition, T>::type* = nullptr>or something similar to that.template<typename T> std::enable_if_t<condition, T> myfunc() {...}or something similar to that.
I understand what's happening in the second, but I'm confused about why anyone would use the first. What does that achieve except add another parameter to the template? Is it an SFINAE thing?
I'm also stuck on universal references when using enable_if. Here is my code and the results I'm getting. Note that I'm using Howard Hinnant's type printing code from "Is it possible to print a variable's type in standard C++?", which I'll omit here for brevity.
Anyways, the function conditionless seems to work fine with everything.
I'm very confused about is_integral and decay, which you can see at thhe beginning of main. I get the output:
true: unsigned long
false: unsigned long
false: unsigned long
false: unsigned long
and I have no idea why the last three are false.
Then I have the issues (marked 1 and 2 in the source below) where when using enable_if in either of the two ways mentioned above, they refuse to compile when accepting an lvalue of an integral or floating point type.
Headers and type printing code omitted for brevity:
template<typename T>
void conditionless(T&& val) {
std::cout << "conditionless(" << val << ")\n";
}
template<typename T, typename std::enable_if<std::is_integral_v<T>, T>::type* = nullptr>
void outputIntType(T&& val) {
std::cout << "outputIntType(" << val << ")\n";
}
template<typename T>
typename std::enable_if_t<std::is_floating_point_v<T>>
outputFloatType(T&& val) {
std::cout << "outputFloatType(" << val << ")\n";
}
int main() {
size_t sz = 1;
size_t &ref = sz;
// All of these report as having type "unsigned long", but for some reason, the first reports true for is_integral, and
// the other three report false.
std::cout << std::boolalpha << std::is_integral_v<decltype(sz)> << ": " << type_name<decltype(sz)>() << '\n';
std::cout << std::boolalpha << std::is_integral_v<std::decay<decltype(sz)>> << ": " << type_name<std::decay<decltype(sz)>::type>() << '\n';
std::cout << std::boolalpha << std::is_integral_v<decltype(ref)> << ": " << type_name<decltype(sz)>() << '\n';
std::cout << std::boolalpha << std::is_integral_v<std::decay<decltype(ref)>> << ": " << type_name<std::decay<decltype(ref)>::type>() <<'\n';
// This works fine.
conditionless(sz);
conditionless(2UL);
conditionless(2L + 1);
// ******* 1 *******
// This fails and claims no matching function call to outputIntType(size_t&)
// template argument deduction / substitution failed:
// error: no type named 'type' in 'struct std::enable_if<false, long unisgned int&>'
// I'm particularly confused about why the is_integral evaluates to false.
//outputIntType(sz);
// These work fine.
outputIntType(2UL);
outputIntType(2L + 1);
double pi = 3.1415926535;
// These work fine.
conditionless(pi);
conditionless(2 * pi);
conditionless(0.00000001);
// ******* 2 *******
// This fails as well:
// main.cpp: In function 'int main()':
// error: no matching function for call to 'outputFloatType(double&)'
// note: candidate: 'template<class T> std::enable_if_t<is_floating_point_v<T> > outputFloatType(T&&)'
// template argument deduction/substitution failed:
// outputFloatType(pi);
// These work fine.
outputFloatType(2 * pi);
outputFloatType(0.00000001);
}
Any insight that anyone could give me on the two different uses of enable_if and why my code with enable_if refuses to accept lvalues would be greatly appreciated.