Обрабатывать enum
s как флаги хорошо работает в C # с помощью [Flags]
атрибута, но как лучше всего это сделать в C ++?
Например, я хотел бы написать:
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
seahawk.flags = CanFly | EatsFish | Endangered;
Тем не менее, я получаю ошибки компилятора относительно int
/ enum
преобразования. Есть ли лучший способ выразить это, чем просто тупое литье? Предпочтительно, я не хочу полагаться на конструкции из сторонних библиотек, таких как boost или Qt.
РЕДАКТИРОВАТЬ: Как указано в ответах, я могу избежать ошибки компилятора, объявив seahawk.flags
как int
. Однако я хотел бы иметь некоторый механизм для обеспечения безопасности типов, чтобы кто-то не мог писать seahawk.flags = HasMaximizeButton
.
[Flags]
атрибут работает просто отлично, т.е.:[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};
Ответы:
«Правильный» способ - определить битовые операторы для перечисления, как:
И т.д. Остальные битовые операторы. При необходимости измените, если диапазон enum превышает int.
источник
AnimalFlags
представлен выражениемHasClaws | CanFly
? Это не тоenum
, для чего. Используйте целые числа и константы.(HasClaws | CanFly)
".HasClaws
(= 1), так иCanFly
(= 2). Если вместо этого вы просто присваиваете значения с 1 по 4 и получаете 3, это может быть одиночнаяEatsFish
или снова комбинацияHasClaws
иCanFly
. Если ваше перечисление обозначает исключительные состояния только тогда, последовательные значения хороши, но комбинация флагов требует, чтобы значения были исключающими бит.Примечание (также немного не по теме): другой способ создания уникальных флагов можно сделать с помощью сдвига битов. Мне самому легче это читать.
Он может содержать значения вплоть до int, то есть, в большинстве случаев, 32 флага, что четко отражается в величине смещения.
источник
Для ленивых людей, как я, вот шаблонное решение для копирования и вставки:
источник
using
где это уместно, просто такrel_ops
.Обратите внимание: если вы работаете в среде Windows,
DEFINE_ENUM_FLAG_OPERATORS
в winnt.h определен макрос, который сделает эту работу за вас. Так что в этом случае вы можете сделать это:источник
К какому типу относится переменная seahawk.flags?
В стандартном C ++ перечисления не являются типобезопасными. Они эффективно целые числа.
AnimalFlags НЕ должен быть типом вашей переменной. Ваша переменная должна быть int и ошибка исчезнет.
Ввод шестнадцатеричных значений, как предлагали некоторые другие люди, не требуется. Это не имеет значения.
Перечисляемые значения являются типом int по умолчанию. Таким образом, вы можете, конечно, побитовый ИЛИ объединить их и собрать их вместе и сохранить результат в int.
Тип enum - это ограниченное подмножество int, значением которого является одно из его перечисляемых значений. Следовательно, когда вы создаете какое-то новое значение за пределами этого диапазона, вы не можете присвоить его без приведения к переменной вашего типа enum.
Вы также можете изменить типы значений enum, если хотите, но для этого вопроса нет смысла.
РЕДАКТИРОВАТЬ: автор сказал, что они были обеспокоены безопасностью типов и они не хотят, чтобы значение, которое не должно существовать внутри типа int.
Но было бы небезопасно помещать значение вне диапазона AnimalFlags внутри переменной типа AnimalFlags.
Существует безопасный способ проверки значений вне диапазона, хотя внутри типа int ...
Вышеприведенное не мешает вам поставить неверный флаг из другого перечисления со значением 1,2,4 или 8.
Если вы хотите абсолютной безопасности типов, тогда вы можете просто создать std :: set и сохранить каждый флаг внутри себя. Это не экономит место, но является безопасным типом и дает вам те же возможности, что и битовый флаг int.
C ++ 0x примечание: строго типизированные перечисления
В C ++ 0x вы, наконец, можете получить безопасные для перечисления значения enum ....
источник
HasClaws | CanFly
- это некоторый целочисленный тип, но типHasClaws
- этоAnimalFlags
не целочисленный тип.max(|emin| − K, |emax|)
и равное(1u<<M) - 1
, гдеM
неотрицательное целое число. "int
.enum
умолчанию технически не являетсяint
базовым типом (либо до C ++ 11 (IIRC), либо после C ++ 11, когда не указан базовый тип), хотяenum class
это так и есть . Вместо этого базовый тип по умолчанию равен чему-то достаточно большому, чтобы представлять все перечислители, с единственным реальным жестким правилом, которое только больше, чемint
если бы оно явно требовалось . По сути, базовый тип указывается как (перефразировано) «все, что работает, но, вероятно,int
если перечислители не слишком велики дляint
».Я считаю, что принятый в настоящее время ответ Эйдолона слишком опасен. Оптимизатор компилятора может делать предположения о возможных значениях в перечислении, и вы можете получить мусор обратно с недопустимыми значениями. И обычно никто не хочет определять все возможные перестановки в перечислениях флагов.
Как утверждает Брайан Р. Бонди ниже, если вы используете C ++ 11 (что должно быть всем, это хорошо), теперь вы можете сделать это проще с помощью
enum class
:Это обеспечивает стабильный диапазон размеров и значений путем указания типа для перечисления, запрещает автоматическое преобразование перечислений в целочисленные значения и т. Д. С помощью
enum class
и используетconstexpr
для обеспечения того, чтобы код для операторов вставлялся и, следовательно, так же быстро, как и обычные числа.Для людей, придерживающихся диалектов C ++ до 11
Если бы я застрял с компилятором, который не поддерживает C ++ 11, я бы пошел с переносом типа int в класс, который затем разрешает использовать только побитовые операторы и типы из этого перечисления для установки его значений:
Вы можете определить это как обычный enum + typedef:
И использование также похоже:
И вы также можете переопределить базовый тип для двоично-устойчивых перечислений (как C ++ 11 - х
enum foo : type
) с помощью второго параметра шаблона, то естьtypedef SafeEnum<enum TFlags_,uint8_t> TFlags;
.Я пометил
operator bool
переопределениеexplicit
ключевым словом C ++ 11, чтобы оно не приводило к преобразованиям int, так как это может привести к тому, что наборы флагов в конечном итоге свернуты в 0 или 1 при их записи. Если вы не можете использовать C ++ 11, не используйте эту перегрузку и переписайте первое условие в примере использования как(myFlags & EFlagTwo) == EFlagTwo
.источник
std::underlying_type
вместо жесткого кодирования определенный тип, или чтобы базовый тип предоставлялся и использовался как псевдоним типа, а не напрямую. Таким образом, изменения в базовом типе будут распространяться автоматически, а не вручную.Самый простой способ сделать это, как показано здесь , с использованием стандартного набора классов библиотеки .
Чтобы эмулировать функцию C # безопасным для типов способом, вам нужно написать оболочку шаблона вокруг набора битов, заменив аргументы int на перечисление, заданное в качестве параметра типа для шаблона. Что-то вроде:
источник
На мой взгляд, ни один из ответов пока не идеален. Чтобы быть идеальным, я ожидал бы решения:
==
,!=
,=
,&
,&=
,|
,|=
и~
операторы в обычном смысле (то естьa & b
)if (a & b)...
Пока что большинство решений относятся к пунктам 2 или 3. На мой взгляд, WebDancer - закрытие, но в пункте 3 он терпит неудачу и должен повторяться для каждого перечисления.
Мое предлагаемое решение - это обобщенная версия WebDancer, которая также касается пункта 3:
Это создает перегрузки необходимых операторов, но использует SFINAE, чтобы ограничить их перечисляемыми типами. Обратите внимание, что в интересах краткости я не определил все операторы, но отличается только один
&
. В настоящее время операторы являются глобальными (т.е. применяются ко всем перечисляемым типам), но это можно уменьшить либо путем помещения перегрузок в пространство имен (что я делаю), либо путем добавления дополнительных условий SFINAE (возможно, с использованием определенных базовых типов или специально созданных псевдонимов типов). ). Этоunderlying_type_t
функция C ++ 14, но, похоже, она хорошо поддерживается и ее легко эмулировать для C ++ 11 с помощью простогоtemplate<typename T> using underlying_type_t = underlying_type<T>::type;
источник
Стандарт C ++ прямо говорит об этом, см. Раздел «17.5.2.1.3 Типы битовых масок»:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf
Учитывая этот «шаблон» вы получаете:
И похоже на других операторов. Также обратите внимание на «constexpr», он необходим, если вы хотите, чтобы компилятор мог выполнять операторы во время компиляции.
Если вы используете C ++ / CLI и хотите иметь возможность присваивать перечислимым элементам классов ref, вам нужно вместо этого использовать отслеживание ссылок:
ПРИМЕЧАНИЕ. Этот пример неполон, см. Раздел «17.5.2.1.3 Типы битовой маски» для полного набора операторов.
источник
Я обнаружил, что задаю тот же вопрос, и придумал общее решение на C ++ 11, похожее на сору:
Интерфейс может быть улучшен по вкусу. Тогда это можно использовать так:
источник
Если ваш компилятор еще не поддерживает строго типизированные перечисления, вы можете посмотреть следующую статью из источника c ++:
Из аннотации:
источник
Я использую следующий макрос:
Он похож на упомянутые выше, но имеет несколько улучшений:
int
)Нужно включить type_traits:
источник
Я хотел бы остановиться на ответе Uliwitness , исправляя его код для C ++ 98 и используя идиому Safe Bool , из-за отсутствия
std::underlying_type<>
шаблона иexplicit
ключевого слова в версиях C ++ ниже C ++ 11.Я также изменил его так, чтобы значения перечисления могли быть последовательными без какого-либо явного присваивания, так что вы можете иметь
Затем вы можете получить значение необработанных флагов с помощью
Вот код
источник
Вот вариант для битовых масок, если вы на самом деле не используете отдельные значения перечисления (например, вам не нужно их отключать) ... и если вы не беспокоитесь о поддержке бинарной совместимости, т.е. не волнует, где живут ваши биты ... что вы, вероятно,. Кроме того, вы не должны быть слишком обеспокоены областью видимости и контролем доступа. Хм, у перечислений есть хорошие свойства для битовых полей ... интересно, кто-нибудь когда-нибудь пробовал это :)
Мы видим, что жизнь прекрасна, у нас есть свои дискретные ценности, и у нас есть хороший int для & и | к нашему сердцу содержание, которое все еще имеет контекст того, что означают его биты. Все непротиворечиво и предсказуемо ... для меня ... пока я продолжаю использовать компилятор Microsoft VC ++ с обновлением 3 на Win10 x64 и не трогаю флаги моего компилятора :)
Несмотря на то, что все отлично ... у нас есть некоторый контекст относительно значения флагов, так как он находится в единстве с битовым полем в ужасном реальном мире, где ваша программа может быть ответственна за более чем одну дискретную задачу, которую вы могли бы все еще случайно (довольно легко) разбить два поля флагов различных объединений (скажем, AnimalProperties и ObjectProperties, так как они оба целые), перепутав все ваши биты, что является ужасной ошибкой для отслеживания ... и насколько я знаю многие люди в этом посте не очень часто работают с битовыми масками, поскольку их легко создать, а поддерживать их сложно.
Итак, вы делаете свою декларацию union закрытой, чтобы предотвратить прямой доступ к «Flags», и должны добавить геттеры / сеттеры и перегрузки операторов, а затем создать макрос для всего этого, и вы в основном вернулись к тому, с чего начинали, когда пытались сделать это с помощью Enum.
К сожалению, если вы хотите, чтобы ваш код был переносимым, я не думаю, что есть какой-либо способ либо A) гарантировать битовую компоновку, либо B) определить битовую компоновку во время компиляции (чтобы вы могли отслеживать ее и, по крайней мере, исправлять изменения версии / платформы и т. д.) Смещение в структуре с битовыми полями
Во время выполнения вы можете играть трюки с установкой полей и установкой флагов XOR, чтобы увидеть, какие биты изменились, для меня это звучит довольно глупо, хотя стихи имеют 100% согласованное, независимое от платформы и полностью детерминированное решение, например: ENUM.
TL; DR: не слушайте ненавистников. C ++ не английский. Тот факт, что буквальное определение сокращенного ключевого слова, унаследованного от C, может не подходить для вашего использования, не означает, что вы не должны использовать его, когда определение ключевого слова на C и C ++ полностью включает ваш вариант использования. Вы также можете использовать структуры, чтобы моделировать вещи, отличные от структур, и классы для вещей, отличных от школьной и социальной касты. Вы можете использовать float для значений, которые обоснованы. Вы можете использовать char для переменных, которые не являются ни сожженными, ни людьми в романе, пьесе или фильме. Любой программист, который идет в словарь, чтобы определить значение ключевого слова до того, как спецификация языка ... ну, я буду там молчать.
Если вы хотите, чтобы ваш код был смоделирован на разговорном языке, лучше всего было бы писать в Objective-C, который, кстати, также активно использует перечисления для битовых полей.
источник
Только синтаксический сахар. Никаких дополнительных метаданных.
Операторы флагов на целочисленном типе просто работают.
источник
В настоящее время нет языковой поддержки флагов enum, мета-классы могут добавить эту функцию, если она когда-либо будет частью стандарта c ++.
Мое решение состояло бы в том, чтобы создать экземпляры шаблонных функций только для перечисления, добавив поддержку побитовых безопасных операций для класса перечисления, используя его базовый тип:
Файл: EnumClassBitwise.h
Для удобства и уменьшения количества ошибок вы можете заключить операции с битовыми флагами в перечисления, а также в целые числа:
Файл: BitFlags.h
Возможное использование:
источник
@Xaqq предоставил действительно хороший способ типобезопасный использовать перечислений флаги здесь по
flag_set
классу.Я опубликовал код в GitHub , использование выглядит следующим образом:
источник
Вы путаете объекты и коллекции объектов. В частности, вы путаете двоичные флаги с наборами двоичных флагов. Правильное решение будет выглядеть так:
источник
Вот мое решение без необходимости перегрузки или приведения:
Я думаю, что все в порядке, потому что мы идентифицируем (не строго типизированные) перечисления и целые числа в любом случае.
Так же, как (более длинное) примечание, если вы
Я бы придумал это:
используя списки инициализаторов C ++ 11 и
enum class
.источник
using Flags = unsigned long
внутреннее пространство имен или структуру, содержащую сами значения флага, как/*static*/ const Flags XY = 0x01
и так далее.Как указано выше (Кай) или сделайте следующее. Действительно перечисления являются «перечислениями», то, что вы хотите сделать, это иметь набор, поэтому вы должны действительно использовать stl :: set
источник
Может быть, как NS_OPTIONS Objective-C.
источник