如何在不指定枚举的情况下将 SFINAE 用于枚举?



使用答案来自 枚举的替换失败不是错误 (SFINAE) 我试图编写一个代码来从类中获取枚举值,如果找不到这个枚举值,它将有一个回退值。 由于我是模板的初学者,几个小时后我放弃了,并使用宏找到了"解决方案":(

有没有办法在没有宏的情况下做同样的事情,也不为每个可能的枚举值复制代码?

这就是我想出的:

struct foo
{
enum FooFields
{
enumFoo,
enumHehe
};
};
struct bar
{
enum BarFields
{
enumHehe = 2
};
};
#define GETENUM_DEF(testedEnum) 
template<class T> 
struct get_ ## testedEnum{
typedef char yes;
typedef yes (&no)[2];

template<int>
struct test2;

template<class U>
static int test(test2<U::testedEnum>*){return U::testedEnum;};
template<class U>
static int test(...){return -1;};

static int value(){return test<T>(0);}
};
GETENUM_DEF(enumFoo)
GETENUM_DEF(enumHehe)
int main() {
std::cout<<get_enumFoo<foo>::value()<<std::endl; //returns 0;
std::cout<<get_enumFoo<bar>::value()<<std::endl; //returns -1;
std::cout<<get_enumHehe<foo>::value()<<std::endl; //returns 1;
std::cout<<get_enumHehe<bar>::value()<<std::endl; //returns 2;
return 0;
}

>C++要求您为要获取的每个字段定义一个get_someField,但您必须在有或没有宏的情况下执行此操作。

使用 SFINAE 是所谓的模板元编程的一部分。您正在做的是有效地检测表达式T::enumFoo是否有效,如果有效,则返回该值,否则-1

为了检测表达式是否有效,我们可以做这样的事情:

#include <type_traits>
// You need void_t to avoid a warning about the lhs of the comma operator 
// having no effect. C++ 17 has std::void_t
template<class...> using void_t = void;
template<class T, class = void>
struct get_enumFoo
{
static constexpr int value = -1;
};
template<class T>
struct get_enumFoo<T, void_t<decltype(T::enumFoo)>>
{
static constexpr int value = T::enumFoo;
};
template<class T, class = void>
struct get_enumHehe
{
static constexpr int value = -1;
};
template<class T>
struct get_enumHehe<T, void_t<decltype(T::enumHehe)>>
{
static constexpr int value = T::enumHehe;
};

使用它(您的示例):

#include <iostream>
int main() {
std::cout << get_enumFoo<foo>::value << std::endl; //returns 0;
std::cout << get_enumFoo<bar>::value << std::endl; //returns -1;
std::cout << get_enumHehe<foo>::value << std::endl; //returns 1;
std::cout << get_enumHehe<bar>::value << std::endl; //returns 2;
}

它的工作原理如下:

我们定义了一个案例,让 SFINAE 在表达式T::enumFoo无效的情况下回退:

template<class T, class = void>
struct get_enumFoo
{
static constexpr int value = -1;
};

然后,我们定义查看T::enumFoo是否有效的案例:

template<class T>
struct get_enumFoo<T, void_t<decltype(T::enumFoo)>>
{
static constexpr int value = T::enumFoo;
};

如果T::enumFoo无效,则decltype(T::enumFoo)是无效表达式,因此 SFINAE 启动,我们回退到之前的情况。

如果T::enumFoo有效,则decltype(T::enumFoo)是某种类型,但void_t<decltype(T::enumFoo)>是无效的。因此,我们专注于get_enumFoo<T, void>拥有该领域的value = T::enumFoo

在线试用


若要进一步减少添加新get_field特征背后的样板文件,可以定义一个基类:

namespace detail {
struct get_foo_defaultValue
{
static constexpr int value = -1;
};
}

那么基本情况将是

template<class T, class = void>
struct get_enumFoo
: detail::get_foo_defaultValue
{};

相关内容

  • 没有找到相关文章

最新更新