Как заставить класс enum работать с функцией «бит или»?

Я обычно использую enum с 'bit-or' или | вместе, чтобы позволить объекту иметь некоторые параметры. Как заставить класс enum работать с функцией «бит или»?


person user1899020    schedule 14.09.2013    source источник
comment
Не должно быть разницы, которую вам, возможно, придется привести к int, об этом   -  person aaronman    schedule 14.09.2013
comment
Некоторые ответы на этот вопрос могут вас заинтересовать: Как установить, очистить и переключить один бит в C?.   -  person DavidRR    schedule 14.09.2013


Ответы (1)


Вам необходимо перегрузить операторы для вашего класса enum и реализовать их путем приведения к базовому типу:

enum class foo : unsigned {
    bar = 1,
    baz = 2
};

foo operator |(foo a, foo b) {
    return static_cast<foo>(static_cast<unsigned>(a) | static_cast<unsigned>(b));
}

… конечно, это можно обобщить (используя SFINAE и std::underlying_type). То, что C++ не предоставляет этого из коробки, на мой взгляд, является упущением.

Вот как может выглядеть общая реализация:

// Intentionally undefined for non-enum types.
template <typename T, bool = std::is_enum<T>::value>
struct is_flag;

template <typename T>
struct is_flag<T, true> : std::false_type { };

template <typename T, typename std::enable_if<is_flag<T>::value>::type* = nullptr>
T operator |(T lhs, T rhs) {
    using u_t = typename std::underlying_type<T>::type;
    return static_cast<T>(static_cast<u_t>(lhs) | static_cast<u_t>(rhs));
}

// … same for `&`, `~`. And maybe functions like `isset`, `set` and `unset`.

Эта реализация гарантирует, что перегрузка будет найдена только для перечислений, которые фактически действуют как флаги. Чтобы пометить перечисление как флаг, вам нужно специализировать is_flag:

enum class a_flag : unsigned {
    foo = 0,
    bar = 1,
    baz = 2
};

template <> struct is_flag<a_flag> : std::true_type { };
person Konrad Rudolph    schedule 14.09.2013
comment
That C++ doesn’t provide this out of the box is an oversight, in my opinion. Перечисления не являются битовыми полями. Это перечисления. - person Lightness Races in Orbit; 14.09.2013
comment
@LightnessRacesinOrbit Кажется, вы утверждаете, что использование перечислений для битовых флагов не является широко используемым устоявшимся шаблоном в C ++. Между прочим, C# идет еще дальше и предоставляет явную поддержку для обработки определенных перечислений как битовых флагов. - person Konrad Rudolph; 14.09.2013
comment
Я никогда не утверждал, что это не широко используемый или устоявшийся шаблон. Я хочу сказать, что этого не должно быть, потому что это не то, что перечисления в C++. То, что ИЛИ с ними не встроено в C++, только усиливает это. Я очень рад, что в C# есть своя версия этой языковой функции с другой семантикой, но C# — это не C++. - person Lightness Races in Orbit; 15.09.2013
comment
@Lightness Но почему этого не должно быть? Это очень практичная идиома, я не вижу проблемы. В лучшем случае вы могли бы сказать, что это смешивание интерфейса и реализации, но это чистая чрезмерная инженерия, и ее также можно тривиально смягчить, предоставив удобные функции, как указано в комментарии к коду в моем ответе. - person Konrad Rudolph; 15.09.2013
comment
Почему бы и нет? Я не могу ответить на это. Все, что я могу вам сказать, это то, как является C++, и, поскольку функция enum стоит в C++, то, что op| не является встроенным для перечислений с областью действия, не является упущением: это согласованность в действии. - person Lightness Races in Orbit; 15.09.2013
comment
@LightnessRacesinOrbit: проверьте формулировку диапазонов перечисления. Он явно расширяет диапазон, так что значения перечисления, объединенные по ИЛИ вместе, также находятся в диапазоне. Не случайно, CWG явно намеревалась это сделать. (октябрь 2004 г., Редмонд, IIRC). Мне нужен синтаксис enum class Foo { operator| = default; }. - person MSalters; 16.09.2013
comment
@MSalters: Хорошо, я признаю это и утверждаю, что это еще один пример того, как CWG немного нарушает парадигму C++ за последние несколько лет, причем делает это непоследовательно. - person Lightness Races in Orbit; 16.09.2013
comment
@LightnessRacesinOrbit: Ваше мнение, конечно. В то время CWG считала это устоявшейся парадигмой, и, честное слово, я видел подобное использование перечислений в коде Bell Labs примерно с 1985 года. Помимо вашего мнения, я еще не видел каких-либо четких указаний, какими должны быть перечисления и особенно почему. Какая польза от запрета использования перечислений для этой цели, и оправдывает ли это взлом разумного кода, который компилировался десятилетиями? - person MSalters; 16.09.2013
comment
@MSalters: Сначала давайте разберем соломенное чучело в вашем аргументе: кто сказал что-либо о запрете чего-либо? - person Lightness Races in Orbit; 17.09.2013
comment
@LightnessRacesinOrbit: Вы во всех комментариях спорите о том, каким должен быть C++. Если бы CWG изложила ваше мнение в стандарте — вместо того, чтобы нарушать парадигму C++ — тогда перечисления как битовые поля были бы фактически запрещены, потому что вы не могли рассчитывать на то, что они имеют необходимый диапазон для представления всех комбинаций. - person MSalters; 17.09.2013
comment
@MSalters: Вы вряд ли можете утверждать, что это означает, что я предлагаю их запретить. Все, что мы здесь делаем, это обсуждаем, действительно ли отсутствие op| является недосмотром. - person Lightness Races in Orbit; 17.09.2013
comment
@LightnessRacesinOrbit Аргумент вполне логичен: CWG явно ввела формулировку, позволяющую использовать перечисления в качестве флагов. Классы перечислений, которые обычно предназначены для замены перечислений, не можно использовать для флагов «из коробки» (без прыжков через обручи приведения), даже если они содержат такая же формулировка (относительно размера), чтобы их можно было использовать для флагов. Это, по крайней мере, непоследовательно и настоятельно предполагает недосмотр. - person Konrad Rudolph; 17.09.2013
comment
Позвольте мне сформулировать это сильнее: учитывая всю работу, проделанную для того, чтобы operator| давал значение в диапазоне, довольно удивительно, что operator| отсутствует. - person MSalters; 17.09.2013