enable_if 使用 constexpr bool 测试不起作用
enable_if using a constexpr bool test not working
我有一个数学函数,我希望它能够接受双精度数或 array/vector/container 双精度数,并且行为略有不同。
我正在尝试使用 SFINAE 并键入特征以 select 正确的函数。
这是一个最小的例子:
#include <iostream>
#include <vector>
#include <type_traits>
template <typename T>
constexpr bool Iscontainer()
{
if constexpr (std::is_class<T>::value && 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<IsContainer<T>()>::type g(T const & t)
{
std::cout << "this is for a container" << t[0] << std::endl;
}
int main()
{
std::vector<double> v {1, 2};
std::array<double, 2> a {1, 2};
double d {0.1};
g<>(v);
g<>(a);
g<>(d); // error here
}
我遇到编译时错误:
../main.cpp:8:47: error: ‘double’ is not a class, struct, or union type
if constexpr (std::is_class<T>::value && std::is_arithmetic<typename T::value_type>::value) {
~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
但是,当我将函数 2 替换为:
// Function 2 (vec), version 2:
template <typename T>
typename std::enable_if<std::is_class<T>::value && std::is_arithmetic<typename T::value_type>::value>::type
g(T const & t)
{
std::cout << "this is for a vector" << t[0] << std::endl;
}
有效。
我的问题是我不明白为什么第一个版本不起作用..
而且我更喜欢第一个版本的可读性。
似乎简单的重载就可以完成工作:
template <typename T>
void g(T const & t)
{
std::cout << "this is for a double" << t << std::endl;
}
template <typename T>
void g(const std::vector<T>& t)
{
std::cout << "this is for a vector" << t[0] << std::endl;
}
你的问题是 T::value_type
被评估为 double
在短路评估之前,您有硬错误(那里没有发生 SFINAE)。
您可以将此条件放在 constexpr 部分。
您可以重写您的函数:
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;
}
你的第二个版本使用 SFINAE
// Function 2 (vec), version 2:
template <typename T>
typename std::enable_if<std::is_class<T>::value
&& std::is_arithmetic<typename T::value_type>::value>::type
// SFINAE happens here for double ^^^^^^^^^^^^^
g(T const & t)
失败的原因很简单。您不调用 SFINAE,当编译器尝试计算它看到的表达式时:
if constexpr (std::is_class<double>::value // this is fine it's false
&& std::is_arithmetic<typename double::value_type>::value // problem here!
)
整个语句被求值,if没有短路。与您当前所拥有的最接近的解决方案是显式拆分 if
,以便在 T
不是 class 且第二次检查无意义时丢弃有问题的部分。
#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
}
或者我建议使用 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;
}
你也可以给它起个更好的名字。它并没有真正检查 T
是 vector
还是容器(在您编辑之后)。您当前的定义也有点松散。
我有一个数学函数,我希望它能够接受双精度数或 array/vector/container 双精度数,并且行为略有不同。
我正在尝试使用 SFINAE 并键入特征以 select 正确的函数。
这是一个最小的例子:
#include <iostream>
#include <vector>
#include <type_traits>
template <typename T>
constexpr bool Iscontainer()
{
if constexpr (std::is_class<T>::value && 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<IsContainer<T>()>::type g(T const & t)
{
std::cout << "this is for a container" << t[0] << std::endl;
}
int main()
{
std::vector<double> v {1, 2};
std::array<double, 2> a {1, 2};
double d {0.1};
g<>(v);
g<>(a);
g<>(d); // error here
}
我遇到编译时错误:
../main.cpp:8:47: error: ‘double’ is not a class, struct, or union type
if constexpr (std::is_class<T>::value && std::is_arithmetic<typename T::value_type>::value) {
~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~
但是,当我将函数 2 替换为:
// Function 2 (vec), version 2:
template <typename T>
typename std::enable_if<std::is_class<T>::value && std::is_arithmetic<typename T::value_type>::value>::type
g(T const & t)
{
std::cout << "this is for a vector" << t[0] << std::endl;
}
有效。
我的问题是我不明白为什么第一个版本不起作用.. 而且我更喜欢第一个版本的可读性。
似乎简单的重载就可以完成工作:
template <typename T>
void g(T const & t)
{
std::cout << "this is for a double" << t << std::endl;
}
template <typename T>
void g(const std::vector<T>& t)
{
std::cout << "this is for a vector" << t[0] << std::endl;
}
你的问题是 T::value_type
被评估为 double
在短路评估之前,您有硬错误(那里没有发生 SFINAE)。
您可以将此条件放在 constexpr 部分。
您可以重写您的函数:
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;
}
你的第二个版本使用 SFINAE
// Function 2 (vec), version 2:
template <typename T>
typename std::enable_if<std::is_class<T>::value
&& std::is_arithmetic<typename T::value_type>::value>::type
// SFINAE happens here for double ^^^^^^^^^^^^^
g(T const & t)
失败的原因很简单。您不调用 SFINAE,当编译器尝试计算它看到的表达式时:
if constexpr (std::is_class<double>::value // this is fine it's false
&& std::is_arithmetic<typename double::value_type>::value // problem here!
)
整个语句被求值,if没有短路。与您当前所拥有的最接近的解决方案是显式拆分 if
,以便在 T
不是 class 且第二次检查无意义时丢弃有问题的部分。
#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
}
或者我建议使用 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;
}
你也可以给它起个更好的名字。它并没有真正检查 T
是 vector
还是容器(在您编辑之后)。您当前的定义也有点松散。