与两个(看似)不相关的类型匹配的模板

Template matching two (seemingly) unrelated types

本文关键字:类型 看似 两个 不相关      更新时间:2023-10-16

我有一个作用域枚举:

enum class E
{ A, B, C };

现在我想要一个函数,它接受该作用域 int OR 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的类型,而只接受单个枚举或整数的类型。

重载似乎是最简单的:

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<...>::value是一个布尔值,所以你可以简单地使用||运算符,std::is_same<...>::value 2 个:

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);

std::is_same实例化定义了constexpr隐式bool转换,因此您可以实例化它们并使用||执行逻辑OR。在 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 的示例实现对我来说很好用。如果你有机会,你应该看看它是如何工作的。这是相当聪明的!