如何为枚举创建模板运算符

时间:2016-04-22 12:10:38

标签: c++ templates c++11 enumeration enumerate

我需要一个没有宏魔法的通用模板类,我可以这样使用:

AlertDialog.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        builder = new android.app.AlertDialog.Builder(getActivity(),android.R.style.Theme_Material_Light_Dialog_Alert);
} else {
        builder = new android.app.AlertDialog.Builder(getActivity());
}

是否可以使用可变参数模板或其他?我怎样才能做到这一点?

6 个答案:

答案 0 :(得分:5)

101010wowofbob的答案为基础:

template <class E, class = std::enable_if_t<std::is_enum<E>{}>>
E &operator ++ (E &e) {
    return e = static_cast<E>(
        static_cast<std::underlying_type_t<E>>(e) + 1
    );
}

我已经SFINAE离开运营商以获取非枚举的所有内容,并添加了适当的static_cast,以便它也适用于enum class es。

Live on Coliru

如果您不想授予所有enum在阳光下的增加能力,您可以将此运算符嵌套在命名空间中(让我们说::incr_enum ),然后你可以:

  • 按照101010建议在该命名空间中声明您的枚举,在这种情况下,通过ADL找到运算符;
  • using namespace incr_enum;当您想要在本地范围内导入和使用该运算符时。

答案 1 :(得分:2)

您无法从classenum继承enum class。在我看来,你能做的最好的事情就是将你的模板重载运算符定义为一个自由函数,并将它和你想在命名空间中使用它的所有enum放在一起(例如fancy)并让名字查找完成剩下的工作:

namespace fancy {

enum colors  { white, red, green, blue };
enum corners { topleft, topright, bottomleft, bottomright };

template<typename E>
E& operator++(E &e) {
  e = static_cast<E>(static_cast<int>(e) + 1);
  return e;
}

} // end of namespace fancy

通过这种方式,您可以限制您的运营商仅使用您在命名空间中拥有的内容。

Live Demo

答案 2 :(得分:1)

不要使用operator ++!如果你有这样的枚举,你该怎么办?:

enum class other_enum : int
{
    low = -3000,
    fabada_asturiana = 0xfabada,
    answer_to_life_universe_everything = 0b101010,
    high = -low
};

正如您所看到的,这些值无法达到前一个值,并且甚至没有模式;请改用迭代器。基于this answer

// Shortcut to the enum map.
template <typename ENUM>
using enum_map = std::map<ENUM, const std::string>;

// The enum map.
template <typename ENUM>
enum_map<ENUM> enum_values{};

// Empty function to end the recursion.
void initialize() {}

// Initialize the enum map.
template <typename ENUM, typename ... args>
void initialize(const ENUM value, const char *name, args ... tail)
{
    enum_values<ENUM>.emplace(value, name);
    initialize(tail ...);
}

// Obtain the begin iterator to the enum
template <class ENUM, class = std::enable_if_t<std::is_enum<ENUM>{}>>
auto begin(ENUM &)
{
    return enum_values<ENUM>.begin();
}

// Obtain the end iterator to the enum
template <class ENUM, class = std::enable_if_t<std::is_enum<ENUM>{}>>
auto end(ENUM &)
{
    return enum_values<ENUM>.end();
}

上面的代码允许用户使用枚举值和描述字符串创建地图,它可以像这样使用:

int main()
{
    initialize
    (
        white, "White",
        red,   "Red",
        green, "Green",
        blue,  "Blue",

        topleft,     "Top Left",
        topright,    "Top Right",
        bottomleft,  "Bottom Left",
        bottomright, "Bottom Right",

        other_enum::low, "Lowest value",
        other_enum::fabada_asturiana, "Typical Spanish",
        other_enum::answer_to_life_universe_everything, "42",
        other_enum::high, "Higher value"
    );

    ...
    return 0;
}

initialize电话是强制性的,以使整个事情发挥作用;不需要将所有枚举放在一个电话中。

使用上面的所有代码,我们可以通过这种方式迭代枚举(Live demo):

for (const auto &v : colors{})
    std::cout << v.first << '\n'; // Print values
for (const auto &v : corners{})
    std::cout << v.second << '\n'; // Print names
for (const auto &v : other_enum{})
    std::cout << (int)v.first << " = " << v.second << '\n'; // Print values & names

使用这种方法,您可以避免继承并直接使用类型。如果您需要保留枚举成员的顺序,可以将enum_map更改为保留订单的容器,或者如果您不需要将值与字符串关联,则可以将基础容器更改为向量或一个清单。

答案 3 :(得分:0)

你真的不需要这里的课程。

这很好用:

#include <assert.h>

enum colors
{
    white,
    red,
    green,
    blue
};

enum corners
{
    topleft,
    topright,
    bottomleft,
    bottomright
};


template<class Enum>
Enum next(Enum e) {
    // To cast back to Enum from `int`
    return static_cast<Enum>(e + 1);
}

int main() {

    colors c = white;
    colors d = next(c);
    assert(c == white);
    assert(d == red);

}

答案 4 :(得分:0)

namespace operator_support {
  template<class T> struct tag_t{using type=T; constexpr tag_t(){}};
  template<class T> constexpr tag_t<T> tag = {};

  template<class T>
  std::false_type operator_support::supports_plus_plus( tag_t<T> );

  template<class E>
  decltype(supports_plus_plus( tag<E> )) supports_plus_plus_v = {};
}

为了使您的枚举supports_plus_plus_t<E>成为true_type,只需将std::true_type supports_plus_plus< tag_t<E> >();写在与枚举E相同的命名空间中即可。 ADL完成其余的工作。

namespace plus_plus {
  template <class E,
    std::enable_if_t<operator_support::supports_plus_plus_v<E>>* =nullptr
  >
  E &operator ++ (E &e) {
    return e = static_cast<E>(
      static_cast<std::underlying_type_t<E>>(e) + 1
    );
  }
}

要使用++,您必须:

  • 在枚举std::true_type supports_plus_plus< tag_t<E> >();的命名空间中输入E

  • using namespace ::plus_plus;您要在++上致电enum。 这限制了++对支持它的枚举的使用,并处理了运算符查找问题。 (来自@csbako的++的正文)。

你可以制作一个宏:

#define PLUS_PLUS_POWERS_ACTIVATE(...) \
  std::true_type supports_plus_plus< tag_t<__VA_ARGS__> >(); \
  using namespace ::plus_plus

采用枚举名称(我使用va args作为C ++枚举名称可能在, s之间嵌入了<>,而宏不喜欢这些名称。

enum whatever {
  a,b,c
};
PLUS_PLUS_POWERS_ACTIVATE(whatever);
int main() {
  whatever x = a;
  std::cout << x;
  ++x;
  std::cout << x;
  ++x;
  std::cout << x;
}

答案 5 :(得分:-1)

namespace enum_operator
{
    template<typename E>
    E& operator++(E& e)
    {
        e = static_cast< E >(static_cast< int >( e ) + 1);
        return e;
    }
};

namespace
{
    enum colors
    {
        white,
        red,
        green,
        blue
    };

    enum corners
    {
        topleft,
        topright,
        bottomleft,
        bottomright
    };

    using namespace enum_operator
};