模板匹配两个(看似)不相关的类型
Template matching two (seemingly) unrelated types
我有一个作用域枚举:
enum class E
{ A, B, C };
现在我想要一个函数,它接受该作用域 int 的值或 int
本身。
应该是这样的:
template <typename T, std::enable_if_t<std::is_same<T, enum E **OR** int>::value, int> = 0 >
void foo(T value);
但我不知道如何处理 C++ 模板中的 OR 概念。我知道 std::is_convertible
,但我什至不知道,如果我可以在这里使用它,因为你只能 static_cast
作用域 enum
s 到 int.
但无论如何,我不想接受任何可转换为 int
的类型,而只接受单个枚举或整数的类型。
因为 std::is_same<...>::value
是一个布尔值,你可以简单地使用 ||
运算符和 2 std::is_same<...>::value
:
template <typename T, std::enable_if_t<std::is_same<T, enum E>::value || std::is_same<T, int>::value, int> = 0 >
void foo(T value);
重载似乎是最简单的:
void foo(int value);
void foo(E value) { foo(static_cast<int>(value); } // Or specific code
template <typename T> void foo(T) = delete; // To forbid type convertible to int
否则你可以使用 SFINAE
template <typename T>
std::enable_if_t<std::is_same<int, T>::value || std::is_same<E, T>::value>
foo(T value);
std::is_same
实例化定义了一个 constexpr
隐式 bool
转换,因此您可以实例化它们并与 ||
执行逻辑或。在 C++17 中,您还可以使用 std::disjunction
来达到类似的效果,尽管这可能只针对两种类型特征编译得更慢。两者的例子:
#include <type_traits>
enum class E
{ A, B, C };
template <typename T, std::enable_if_t<
std::is_same<T, E>{} || std::is_same<T, int>{},
int> = 0>
void foo(T){
}
//in C++17, you can also do this:
template <typename T, std::enable_if_t<
std::disjunction<std::is_same<T, E>, std::is_same<T, int>>{},
int> = 0>
void bar(T){
}
int main() {
foo(E::A);
foo(0);
//foo('A'); fails
bar(E::A);
bar(0);
//bar('A'); fails
return 0;
}
std::disjunction
是您想知道的逻辑 OR 模板(尽管我建议您将 ||
与 std::is_same
结合使用)。有趣的是,std::disjunction
甚至执行模板实例化的逻辑短路,就像不起眼的旧 ||
运算符在运行时上下文中所做的那样。我相信最新版本的 libc++ 已经与 std::disjunction
一起发布。如果您的 <type_traits>
实现还没有,cppreference 中的示例实现对我来说就很好用。如果有机会,您应该看看它是如何工作的。还挺聪明的!
我有一个作用域枚举:
enum class E
{ A, B, C };
现在我想要一个函数,它接受该作用域 int 的值或 int
本身。
应该是这样的:
template <typename T, std::enable_if_t<std::is_same<T, enum E **OR** int>::value, int> = 0 >
void foo(T value);
但我不知道如何处理 C++ 模板中的 OR 概念。我知道 std::is_convertible
,但我什至不知道,如果我可以在这里使用它,因为你只能 static_cast
作用域 enum
s 到 int.
但无论如何,我不想接受任何可转换为 int
的类型,而只接受单个枚举或整数的类型。
因为 std::is_same<...>::value
是一个布尔值,你可以简单地使用 ||
运算符和 2 std::is_same<...>::value
:
template <typename T, std::enable_if_t<std::is_same<T, enum E>::value || std::is_same<T, int>::value, int> = 0 >
void foo(T value);
重载似乎是最简单的:
void foo(int value);
void foo(E value) { foo(static_cast<int>(value); } // Or specific code
template <typename T> void foo(T) = delete; // To forbid type convertible to int
否则你可以使用 SFINAE
template <typename T>
std::enable_if_t<std::is_same<int, T>::value || std::is_same<E, T>::value>
foo(T value);
std::is_same
实例化定义了一个 constexpr
隐式 bool
转换,因此您可以实例化它们并与 ||
执行逻辑或。在 C++17 中,您还可以使用 std::disjunction
来达到类似的效果,尽管这可能只针对两种类型特征编译得更慢。两者的例子:
#include <type_traits>
enum class E
{ A, B, C };
template <typename T, std::enable_if_t<
std::is_same<T, E>{} || std::is_same<T, int>{},
int> = 0>
void foo(T){
}
//in C++17, you can also do this:
template <typename T, std::enable_if_t<
std::disjunction<std::is_same<T, E>, std::is_same<T, int>>{},
int> = 0>
void bar(T){
}
int main() {
foo(E::A);
foo(0);
//foo('A'); fails
bar(E::A);
bar(0);
//bar('A'); fails
return 0;
}
std::disjunction
是您想知道的逻辑 OR 模板(尽管我建议您将 ||
与 std::is_same
结合使用)。有趣的是,std::disjunction
甚至执行模板实例化的逻辑短路,就像不起眼的旧 ||
运算符在运行时上下文中所做的那样。我相信最新版本的 libc++ 已经与 std::disjunction
一起发布。如果您的 <type_traits>
实现还没有,cppreference 中的示例实现对我来说就很好用。如果有机会,您应该看看它是如何工作的。还挺聪明的!