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}\nRun 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 ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~\nRun 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}\nRun Code Online (Sandbox Code Playgroud)\n\n有用。
\n\n我的问题是我不明白为什么第一个版本不起作用..\n而且我更喜欢第一个版本的可读性。
\n失败的原因很简单。您不调用 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)
您还可以更好地命名它。它并不真正检查是否T是vector, 或容器(编辑后)。您当前的定义也有点宽松。