使用答案来自 枚举的替换失败不是错误 (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
{};