Как создать шаблон оператора для перечислений

Мне нужен общий шаблонный класс без макрокоманды, который я могу использовать так:

template<typename E>
class enum_operators
{
E& operator++( E& orig )
{
orig = static_cast< E >( orig + 1 );
return orig;
}
};

enum colors : public enum_operators< colors >
{
white,
red,
green,
blue
};

enum corners : public enum_operators< corners >
{
topleft,
topright,
bottomleft,
bottomright
};

Возможно ли это с помощью шаблона Variadic или чего-то еще? Как я могу это сделать?

3

Решение

Опираясь на 101010и wowofbobОтветы:

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эс тоже.

Жить на Колиру

Если вы не хотите предоставлять все enumЕсли под солнцем есть возможность увеличения, вы можете вложить этот оператор в пространство имен (скажем, ::incr_enum), а затем вы можете:

  • Объявите ваши перечисления в этом пространстве имен, как рекомендует 101010, и в этом случае оператор найден через ADL;
  • using namespace incr_enum; когда вы хотите импортировать и использовать этот оператор в локальной области.
4

Другие решения

Вы не можете наследовать class из enum или же enum class, По моему скромному мнению, лучшее, что вы можете сделать, — это определить свой перегруженный шаблонный оператор как бесплатную функцию и поместить его и все enumЕсли вы хотите работать с ним в пространстве имен (например, fancy) и пусть name lookup сделает все остальное:

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

Не использовать operator ++! Что вы должны делать, если у вас есть такое перечисление ?:

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

Как видите, значения недоступны, увеличиваясь на единицу предыдущего и даже не имеют шаблона; используйте вместо этого итераторы. На основе этот ответ:

// 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 вызов является обязательным для того, чтобы все это работало; хотя не нужно помещать все перечисления в один вызов.

Со всем приведенным выше кодом, мы можем перебирать перечисления следующим образом (Живая демо):

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, вы можете изменить enum_map к контейнеру, который сохраняет порядок, или если вам не нужно связывать значение со строкой, базовый контейнер можно изменить на вектор или список.

1

Вам не нужны занятия здесь.

Это отлично работает:

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

}
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

который принимает имя enum (я использую va args, поскольку имена enum в 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;
}
0
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
};
-1
По вопросам рекламы [email protected]