Реализация операторов для класса enum
после обсуждения вопроса увеличение и уменьшение "класса перечисления", Я хотел бы спросить о возможной реализации арифметических операторов enum class
типы.
пример из исходного вопроса:
enum class Colors { Black, Blue, White, END_OF_LIST };
// Special behavior for ++Colors
Colors& operator++( Colors &c ) {
c = static_cast<Colors>( static_cast<int>(c) + 1 );
if ( c == Colors::END_OF_LIST )
c = Colors::Black;
return c;
}
есть ли способ реализовать арифметические операторы без приведения к типу с уже определенными операторами? Я не могу придумать ни одного, но кастинг беспокоит меня. Слепки обычно указывают на что-то неправильное и должна быть очень веская причина для их использования. Я ожидал бы, что язык позволит реализовать оператор, чтобы быть достижимым без принуждения к определенному типу.
3 ответов
решение не-бросания использовать переключатель. Однако, вы можете создать псевдо-переключатель используя шаблоны. Принцип заключается в рекурсивной обработке всех значений перечисления с помощью списка шаблонов (или пакета параметров). Итак, вот 3 метода, которые я нашел.
тест перечисления:
enum class Fruit
{
apple,
banana,
orange,
pineapple,
lemon
};
ванильное переключатель (видео здесь):
/// Non-scalable way
Fruit& operator++(Fruit& f)
{
switch(f)
{
case Fruit::apple: return f = Fruit::banana;
case Fruit::banana: return f = Fruit::orange;
case Fruit::orange: return f = Fruit::pineapple;
case Fruit::pineapple: return f = Fruit::lemon;
case Fruit::lemon: return f = Fruit::apple;
}
}
метод c++03-ish (видео здесь):
template<typename E, E v>
struct EnumValue
{
static const E value = v;
};
template<typename h, typename t>
struct StaticList
{
typedef h head;
typedef t tail;
};
template<typename list, typename first>
struct CyclicHead
{
typedef typename list::head item;
};
template<typename first>
struct CyclicHead<void,first>
{
typedef first item;
};
template<typename E, typename list, typename first = typename list::head>
struct Advance
{
typedef typename list::head lh;
typedef typename list::tail lt;
typedef typename CyclicHead<lt, first>::item next;
static void advance(E& value)
{
if(value == lh::value)
value = next::value;
else
Advance<E, typename list::tail, first>::advance(value);
}
};
template<typename E, typename f>
struct Advance<E,void,f>
{
static void advance(E& value)
{
}
};
/// Scalable way, C++03-ish
typedef StaticList<EnumValue<Fruit,Fruit::apple>,
StaticList<EnumValue<Fruit,Fruit::banana>,
StaticList<EnumValue<Fruit,Fruit::orange>,
StaticList<EnumValue<Fruit,Fruit::pineapple>,
StaticList<EnumValue<Fruit,Fruit::lemon>,
void
> > > > > Fruit_values;
Fruit& operator++(Fruit& f)
{
Advance<Fruit, Fruit_values>::advance(f);
return f;
}
метод C++11-ish (live здесь):
template<typename E, E first, E head>
void advanceEnum(E& v)
{
if(v == head)
v = first;
}
template<typename E, E first, E head, E next, E... tail>
void advanceEnum(E& v)
{
if(v == head)
v = next;
else
advanceEnum<E,first,next,tail...>(v);
}
template<typename E, E first, E... values>
struct EnumValues
{
static void advance(E& v)
{
advanceEnum<E, first, first, values...>(v);
}
};
/// Scalable way, C++11-ish
typedef EnumValues<Fruit,
Fruit::apple,
Fruit::banana,
Fruit::orange,
Fruit::pineapple,
Fruit::lemon
> Fruit_values11;
Fruit& operator++(Fruit& f)
{
Fruit_values11::advance(f);
return f;
}
вы можете расширить, добавив препроцессор, чтобы удалить необходимость повторения списка значений.
каждый оператор в C++ на перечислениях может быть записан без приведения к базовому типу, но результат будет смехотворно подробным.
пример:
size_t index( Colors c ) {
switch(c) {
case Colors::Black: return 0;
case Colors::Blue: return 1;
case Colors::White: return 2;
}
}
Color indexd_color( size_t n ) {
switch(n%3) {
case 0: return Colors::Black;
case 1: return Colors::Blue;
case 2: return Colors::White;
}
}
Colors increment( Colors c, size_t n = 1 ) {
return indexed_color( index(c) + n );
}
Colors decrement( Colors c, size_t n = 1 ) {
return indexed_color( index(c)+3 - (n%3) );
}
Colors& operator++( Colors& c ) {
c = increment(c)
return c;
}
Colors operator++( Colors& c, bool ) {
Colors retval = c;
c = increment(c)
return retval;
}
и умный компилятор сможет превратить их в операции, которые непосредственно на основе интегрального типа.
но приведение к базовому интегральному типу в интерфейс enum class
не плохо. И операторы являются частью интерфейса enum class
.
Если вам не нравится этот цикл через size_t
и считайте его поддельным, вы можете просто написать:
Colors increment( Colors c ) {
switch(c) {
case Colors::Black: return Colors::Blue;
case Colors::Blue: return Colors::White;
case Colors::White: return Colors::Black;
}
}
и аналогично для декремента и реализации increment-by -n
как циклы повторяются increment
.
enum class Colors { Black, Blue, White };
Colors operator++(Colors& color)
{
color = (color == Colors::White) ? Colors::Black : Colors(int(color) + 1);
return color;
}