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

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 作用域 enums 到 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 中的示例实现对我来说就很好用。如果有机会,您应该看看它是如何工作的。还挺聪明的!