使用 constexpr bool 测试的 enable_if 不起作用

ddd*_*dd4 5 c++ templates enable-if constexpr c++11

我有一个数学函数,我希望能够接受双精度数或双精度数数组/向量/容器,并且行为略有不同。

\n\n

我正在尝试使用 SFINAE 和类型特征来选择正确的函数。

\n\n

这是一个最小的例子:

\n\n
#include <iostream>\n#include <vector>\n#include <type_traits>\n\ntemplate <typename T>\nconstexpr bool Iscontainer()\n{\n    if constexpr (std::is_class<T>::value && std::is_arithmetic<typename T::value_type>::value) {\n        return true;\n    }\n    return false;\n}\n\n// Function 1 (double):\ntemplate <typename T>\ntypename std::enable_if<std::is_arithmetic<T>::value>::type g(T const & t)\n{\n    std::cout << "this is for a double" << t << std::endl;\n}\n\n// Function 2 (vec), version 1:\ntemplate <typename T>\ntypename std::enable_if<IsContainer<T>()>::type g(T const & t)\n{\n    std::cout << "this is for a container" << t[0] << std::endl;\n}\n\nint main()\n{\n    std::vector<double> v {1, 2};\n    std::array<double, 2> a {1, 2};\n    double d {0.1};\n\n    g<>(v);\n    g<>(a);\n    g<>(d);  // error here\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我收到编译时错误:

\n\n
../main.cpp:8:47: error: \xe2\x80\x98double\xe2\x80\x99 is not a class, struct, or union type\n     if constexpr (std::is_class<T>::value && std::is_arithmetic<typename     T::value_type>::value) {\n                   ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是,当我将函数 2 替换为:

\n\n
// Function 2 (vec), version 2:\ntemplate <typename T>\ntypename std::enable_if<std::is_class<T>::value && std::is_arithmetic<typename T::value_type>::value>::type\ng(T const & t)\n{\n    std::cout << "this is for a vector" << t[0] << std::endl;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

有用。

\n\n

我的问题是我不明白为什么第一个版本不起作用..\n而且我更喜欢第一个版本的可读性。

\n

luk*_*k32 4

失败的原因很简单。您不调用 SFINAE,并且当编译器尝试计算它看到的表达式时:

if constexpr (std::is_class<double>::value // this is fine it's false
   && std::is_arithmetic<typename double::value_type>::value // problem here!
)
Run Code Online (Sandbox Code Playgroud)

整个语句都被评估,if 没有短路。与您当前拥有的最接近的解决方案是显式拆分if,以便在T不是类时丢弃有问题的部分,并且第二次检查是无意义的。

#include <iostream>
#include <vector>
#include <type_traits>

template <typename T>
constexpr bool IsVector()
{
    if constexpr (std::is_class<T>::value) {
        if constexpr (std::is_arithmetic<typename T::value_type>::value) {
            return true;
        }
    }
    return false;
}

// Function 1 (double):
template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value>::type g(T const & t)
{
    std::cout << "this is for a double" << t << std::endl;
}

// Function 2 (vec), version 1:
template <typename T>
typename std::enable_if<IsVector<T>()>::type g(T const & t)
{
    std::cout << "this is for a vector" << t[0] << std::endl;
}

int main()
{
    std::vector<double> v {1, 2};
    double d {0.1};

    g<>(v);
    g<>(d);  // error here
}
Run Code Online (Sandbox Code Playgroud)

或者我建议一个using别名:

template <typename T>
using IsVector2 = std::conjunction<typename std::is_class<T>, std::is_arithmetic<typename T::value_type> >;

template <typename T>
typename std::enable_if<IsVector2<T>::value>::type g(T const & t)
{
    std::cout << "this is for a vector" << t[0] << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

您还可以更好地命名它。它并不真正检查是否Tvector, 或容器(编辑后)。您当前的定义也有点宽松。