Мне нужен общий шаблонный класс без макрокоманды, который я могу использовать так:
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 или чего-то еще? Как я могу это сделать?
Опираясь на 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
), а затем вы можете:
using namespace incr_enum;
когда вы хотите импортировать и использовать этот оператор в локальной области.Вы не можете наследовать 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
Таким образом, вы ограничите свой оператор в работе только с тем, что есть в вашем пространстве имен.
Не использовать 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
к контейнеру, который сохраняет порядок, или если вам не нужно связывать значение со строкой, базовый контейнер можно изменить на вектор или список.
Вам не нужны занятия здесь.
Это отлично работает:
#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);
}
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;
}
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
};