1

我试图std::enable_if更好地理解通用引用,但我对我的代码中发生的事情有点困惑。

首先,我注意到人们似乎以std::enable_if两种不同的方式使用:

  1. template<typename T, std::enable_if<condition, T>::type* = nullptr>或类似的东西。

  2. template<typename T> std::enable_if_t<condition, T> myfunc() {...}或类似的东西。

我理解第二个发生了什么,但我对为什么有人会使用第一个感到困惑。除了向模板添加另一个参数之外,这实现了什么?这是 SFINAE 的事情吗?

使用enable_if. 这是我的代码和我得到的结果。请注意,我使用的是 Howard Hinnant 的类型打印代码,来自“ Is it possible to print a variable's type in standard C++? ”,为简洁起见,我将在此省略。

无论如何,该功能conditionless似乎适用于一切。

我很困惑is_integraland 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拒绝接受左值的任何见解,将不胜感激。

4

1 回答 1

4

我试图理解通用参考

不鼓励使用该术语。官方术语是“转发引用”


我理解第二个发生了什么,但我对为什么有人会使用第一个感到困惑。除了向模板添加另一个参数之外,这实现了什么?这是 SFINAE 的事情吗?

所做enable_if_t<B, T>的只是评估Tif B == true,否则会产生无效代码。替换期间产生的无效代码不会导致编译错误 (SFINAE)。

enable_if_t只要它受到替换步骤的影响(例如,可以在返回类型、参数列表、模板参数列表中……),出现在哪里并不重要。


我不知道为什么最后三个是假的。

您忘记::typestd::decay转换中访问。您是在比较特征本身,而不是结果。


当接受整数或浮点类型的左值时,它们拒绝编译。

标准中有一条关于扣除转发引用的特殊规则。给定一个转发引用参数T&&,如果函数使用左值调用,则将推T导出为左值引用

你需要在你的特质中考虑到这一点:

typename std::enable_if_t<std::is_floating_point_v<std::remove_reference_t<T>>>
于 2018-10-05T14:28:05.890 回答