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

时间:2016-03-14 15:28:31

标签: c++ templates enums sfinae

我有一个范围枚举:

enum class E
{ A, B, C };

现在我想要一个函数,它接受一个scoped 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_castenum范围限定为int。

但无论如何我不想接受任何可转换为int但只能转换为单个枚举或int的类型。

3 个答案:

答案 0 :(得分:6)

重载似乎最简单:

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

答案 1 :(得分:2)

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

答案 2 :(得分:1)

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的示例实现对我来说就可以了。如果你有机会,你应该看看它是如何工作的。它相当聪明!