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

时间:2017-03-31 18:49:57

标签: c++ enums refactoring template-meta-programming sfinae

使用来自的回答 Substitution failure is not an error (SFINAE) for enum我尝试编写一个代码,从类中获取枚举值,如果找不到此枚举值,则会有一个回退值。 由于我是模板的初学者,经过几个小时后我放弃了并找到了一个使用宏的“解决方案”:(

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

这就是我提出的:

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

1 个答案:

答案 0 :(得分: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;
}

它的工作原理如下:

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

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

Try it online

为了进一步减少添加新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
{};
相关问题