我试图更好地理解通用引用,并且std::enable_if
更好,但是我对代码中发生的事情有些犹豫。
首先,我注意到人们似乎以两种不同的方式使用std::enable_if
:
template<typename T, std::enable_if<condition, T>::type* = nullptr>
或类似的东西。
template<typename T> std::enable_if_t<condition, T> myfunc() {...}
或类似的东西。
我了解第二个事件的发生,但是我对为什么有人会使用第一个感到困惑。除了向模板添加另一个参数之外,这还能实现什么?是SFINAE的东西吗?
在使用enable_if
时,我也坚持使用通用引用。这是我的代码和得到的结果。请注意,我使用的是“ Is it possible to print a variable's type in standard C++?”中的Howard Hinnant的类型打印代码,为简洁起见,在此将省略。
无论如何,函数conditionless
似乎可以正常工作。
我对is_integral
和decay
感到非常困惑,您可以在main
的开头看到它们。我得到了输出:
true: unsigned long
false: unsigned long
false: unsigned long
false: unsigned long
我不知道为什么后三个错误。
然后我有一个问题(在下面的源代码中标记为1和2),当以上述两种方式之一使用enable_if
时,它们在接受整数或浮点类型的左值时拒绝编译
为简洁起见,省略了标题和类型的打印代码:
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);
}
任何人都可以给我介绍enable_if
的两种不同用法以及为什么我的带有enable_if
的代码拒绝接受左值的见解,将不胜感激。
答案 0 :(得分:4)
我正在尝试理解通用参考书
不鼓励使用该术语。正式术语是“转发参考” 。
我了解第二个事件的发生,但是我对为什么有人会使用第一个感到困惑。除了向模板添加另一个参数之外,这还能实现什么?是SFINAE的东西吗?
如果enable_if_t<B, T>
,所有T
都将求值为B == true
,否则将生成无效代码。替换期间生成的无效代码不会导致编译错误(SFINAE)。
enable_if_t
的出现位置并不重要,只要它受替换步骤的影响即可(例如可以位于返回类型,参数列表,模板参数列表等)。
我不知道为什么后三个错误。
您忘记在::type
转换中访问std::decay
。您正在比较特征本身,而不是结果。
当接受整数或浮点类型的左值时,它们拒绝编译。
在标准中有一个关于推论转发引用的特殊规则。给定转发参考参数T&&
,如果使用 lvalue 调用函数,则将推论T
作为左值参考。
您需要在特征中考虑这一点:
typename std::enable_if_t<std::is_floating_point_v<std::remove_reference_t<T>>>