[英]std::enable_if with multiple or-conditions
我正在努力寻找允许多个 OR'ed std::enable_if
条件用于我的模板函数的正确语法。
#include <type_traits>
template <typename T>
using Foo = std::enable_if_t<std::is_same_v<T, int>>;
template <typename T>
using Bar = std::enable_if_t<std::is_same_v<T, float>>;
// Fine - accepts only ints
template <typename T, typename = Foo<T>>
void FooIt(T t) {}
// Fine - accepts only floats
template <typename T, typename = Bar<T>>
void BarIt(T t) {}
template <typename T,
// This does not work
typename = std::enable_if_t<Bar<T>::value || Foo<T>::value>>
void FooOrBarIt(T t) {}
int main() {
FooIt(1);
BarIt(1.0f);
// FooIt(1.0f); // OK - No float version of FooIt
// BarIt(1); // OK - No int version of BarIt
// Not OK - Both fails to compile
// FooOrBarIt(1);
// FooOrBarIt(1.0f);
}
我希望FooOrBarIt
function 通过结合前面的Foo
和Bar
条件接受int
s 和float
s 但没有别的。
请注意,我想避免改变以前的条件,我想按原样组合它们。 任何 C++17 都可以。
当前代码因编译错误而失败,例如:
candidate template ignored: requirement 'std::is_same_v<int, float>' was not satisfied [with T = int]
在 clang。
您正在尝试在enable_if_t
内部使用enable_if_t
,这不是您所需要的。 您需要在 1 enable_if_t
中使用is_same_v
,例如:
template <typename T,
typename = std::enable_if_t<std::is_same_v<T,float> || std::is_same_v<T,int>>>
因此,相应地调整您的using
语句,例如:
#include <type_traits>
template <typename T>
inline constexpr bool Is_Int = std::is_same_v<T, int>;
template <typename T>
using Enable_If_Int = std::enable_if_t<Is_Int<T>>;
template <typename T>
inline constexpr bool Is_Float = std::is_same_v<T, float>;
template <typename T>
using Enable_If_Float = std::enable_if_t<Is_Float<T>>;
// Fine - accepts only ints
template <typename T, typename = Enable_If_Int<T>>
void FooIt(T t) {}
// Fine - accepts only floats
template <typename T, typename = Enable_If_Float<T>>
void BarIt(T t) {}
template <typename T,
typename = std::enable_if_t<Is_Float<T> || Is_Int<T>>>
void FooOrBarIt(T t) {}
int main() {
FooIt(1);
BarIt(1.0f);
// FooIt(1.0f); // OK - No float version of FooIt
// BarIt(1); // OK - No int version of BarIt
// OK - Both compile fine
FooOrBarIt(1);
FooOrBarIt(1.0f);
}
SFINAE 不像逻辑运算符那样安静地工作。 您应该使用std::disjunction
及其对应的std::conjunction
来组合 SFINAE 的逻辑测试。 这些在 C++17 中引入,因此您应该能够将它们与您的设置一起使用。
你的||
运算符适用于bool
类型。
您要查找的bool
类型已存在于您的代码中。 std::is_same_v
始终是bool.
template <typename T, typename = std::enable_if_t<
std::is_same_v<T,int> || std::is_same_v<T,float>
>>* = nullptr
考虑到同时包含Foo<T>
和Bar<T>
的表达式永远无法明确定义。
你正在做货物崇拜 SFINAE。 通过这个,我的意思是你正在使用 SFINAE 模式来实现你需要的东西,而不是理解你为什么要做这些模式。
这通常并没有那么糟糕。 SFINAE 有点疯狂,因为它是一种偶然的特性,它泄漏到语言中并为我们提供了图灵完备的重载解决方案。 我的意思是,上次我检查过,它与模板 class 专业化一起工作的事实实际上并不在标准中。
因此,遵循现有模式使其看起来合理是一个很好的计划。
然而,当你想做一些新的事情时,你必须了解实际发生的事情。
template <typename T>
using Foo = std::enable_if_t<std::is_same_v<T, int>>;
这是一个模板别名,如果T
通过测试,则返回类型void
,如果T
未通过测试,则返回类型失败。
// Fine - accepts only ints
template <typename T, typename = Foo<T>>
void FooIt(T t) {}
你的评论有点不对。 FooIt<double, void>
在这里工作正常。
这里发生的事情是,如果T
不是int
,您的默认类型参数会产生替换失败。 如果T
是int
,则第二个类型参数的默认值为void
。
template <typename T,
// This does not work
typename = std::enable_if_t<Bar<T>::value || Foo<T>::value>>
void FooOrBarIt(T t) {}
当然不是。
类型void
上没有::value
。 Bar<T>
void
或替换失败,对于Foo<T>
也是如此。
都不支持::value
。 所以你得到一个错误。
您的Foo<T>
和Bar<T>
不是很好的模式。 但是你说你想保留它们。
所以我要开始的是:
template<class T, template<class>class Z, class=void, template<class>class...Zs>
struct test{};
template<class T, template<class>class Z>
struct test<T,Z,Z<T>>{
using type=void;
};
template<class T,
template<class>class Z0,
template<class>class Z1,
template<class>class...Zs
>
struct test<T, Z0, Z0<T>, Z1, Zs...>:
test<T, Z1, void, Zs...>
{};
template<class T, template<class>class Z0, template<class>class...Zs>
using test_t = typename test<T, Z0, void, Zs...>::type;
现在test_t
可以用来引信你的特征。
template <typename T,
typename = test_t<T, Bar, Foo>>
void FooOrBarIt(T t) {}
以你熟悉的方式。
请注意,这种风格的 SFINAE 存在一些问题,您无法使用它在FooOrBarIt
的两个重载之间进行调度。 IE,
template<class T, class=Bar<T>>
void bob(T) {}
template<class T, class=Foo<T>>
void bob(T) {}
这会导致重载解析错误。
另一种货物崇拜模式:
template<class T>
using Bar = std::enable_if_t<std::is_same_v<T,int>, bool>;
template<class T>
using Foo = std::enable_if_t<std::is_same_v<T,double>, bool>;
template<class T, Bar<T> =true>
void bob(T) {}
template<class T, Foo<T> =true>
void bob(T) {}
看起来略有不同,但允许上面的两个bob
重载工作。
对于该变体,您必须更改加入它们的方式:
template<Ts...>
using AllTraits = bool;
template <typename T,
AllTraits<Foo<T>, Bar<T>> = true>
void FooOrBarIt(T t) {}
请注意,这个特定的 cargo cult 版本中的=true
不是测试; =false
会产生完全相同的结果。
为了提供对比,C++20 版本:
#include <type_traits>
template<typename T>
concept Foo = std::is_same_v<T, int>;
template<typename T>
concept Bar = std::is_same_v<T, float>;
template<typename T>
concept FooOrBar = Foo<T> || Bar<T>;
template<Foo T>
void FooIt(T) { }
template<Bar T>
void BarIt(T) { }
template<FooOrBar T>
void FooOrBarIt(T) { }
int main() {
FooIt(1); // OK
BarIt(1.0f); // OK
// FooIt(1.0f); // OK - No float version of FooIt
// BarIt(1); // OK - No int version of BarIt
FooOrBarIt(1); // OK
FooOrBarIt(1.0f); // OK
// FooOrBarIt(1.0); // No double version of FooOrBarIt
}
以这种方式组合概念要容易得多。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.