Вот вариант для битовых масок, если вы на самом деле не используете отдельные значения перечисления (например, вам не нужно их отключать) ... и если вы не беспокоитесь о поддержке бинарной совместимости, т.е. не волнует, где живут ваши биты ... что вы, вероятно,. Кроме того, вы не должны быть слишком обеспокоены областью видимости и контролем доступа. Хм, у перечислений есть хорошие свойства для битовых полей ... интересно, кто-нибудь когда-нибудь пробовал это :)
struct AnimalProperties
{
bool HasClaws : 1;
bool CanFly : 1;
bool EatsFish : 1;
bool Endangered : 1;
};
union AnimalDescription
{
AnimalProperties Properties;
int Flags;
};
void TestUnionFlags()
{
AnimalDescription propertiesA;
propertiesA.Properties.CanFly = true;
AnimalDescription propertiesB = propertiesA;
propertiesB.Properties.EatsFish = true;
if( propertiesA.Flags == propertiesB.Flags )
{
cout << "Life is terrible :(";
}
else
{
cout << "Life is great!";
}
AnimalDescription propertiesC = propertiesA;
if( propertiesA.Flags == propertiesC.Flags )
{
cout << "Life is great!";
}
else
{
cout << "Life is terrible :(";
}
}
Мы видим, что жизнь прекрасна, у нас есть свои дискретные ценности, и у нас есть хороший int для & и | к нашему сердцу содержание, которое все еще имеет контекст того, что означают его биты. Все непротиворечиво и предсказуемо ... для меня ... пока я продолжаю использовать компилятор Microsoft VC ++ с обновлением 3 на Win10 x64 и не трогаю флаги моего компилятора :)
Несмотря на то, что все отлично ... у нас есть некоторый контекст относительно значения флагов, так как он находится в единстве с битовым полем в ужасном реальном мире, где ваша программа может быть ответственна за более чем одну дискретную задачу, которую вы могли бы все еще случайно (довольно легко) разбить два поля флагов различных объединений (скажем, AnimalProperties и ObjectProperties, так как они оба целые), перепутав все ваши биты, что является ужасной ошибкой для отслеживания ... и насколько я знаю многие люди в этом посте не очень часто работают с битовыми масками, поскольку их легко создать, а поддерживать их сложно.
class AnimalDefinition {
public:
static AnimalDefinition *GetAnimalDefinition( AnimalFlags flags ); //A little too obvious for my taste... NEXT!
static AnimalDefinition *GetAnimalDefinition( AnimalProperties properties ); //Oh I see how to use this! BORING, NEXT!
static AnimalDefinition *GetAnimalDefinition( int flags ); //hmm, wish I could see how to construct a valid "flags" int without CrossFingers+Ctrl+Shift+F("Animal*"). Maybe just hard-code 16 or something?
AnimalFlags animalFlags; //Well this is *way* too hard to break unintentionally, screw this!
int flags; //PERFECT! Nothing will ever go wrong here...
//wait, what values are used for this particular flags field? Is this AnimalFlags or ObjectFlags? Or is it RuntimePlatformFlags? Does it matter? Where's the documentation?
//Well luckily anyone in the code base and get confused and destroy the whole program! At least I don't need to static_cast anymore, phew!
private:
AnimalDescription m_description; //Oh I know what this is. All of the mystery and excitement of life has been stolen away :(
}
Итак, вы делаете свою декларацию union закрытой, чтобы предотвратить прямой доступ к «Flags», и должны добавить геттеры / сеттеры и перегрузки операторов, а затем создать макрос для всего этого, и вы в основном вернулись к тому, с чего начинали, когда пытались сделать это с помощью Enum.
К сожалению, если вы хотите, чтобы ваш код был переносимым, я не думаю, что есть какой-либо способ либо A) гарантировать битовую компоновку, либо B) определить битовую компоновку во время компиляции (чтобы вы могли отслеживать ее и, по крайней мере, исправлять изменения версии / платформы и т. д.)
Смещение в структуре с битовыми полями
Во время выполнения вы можете играть трюки с установкой полей и установкой флагов XOR, чтобы увидеть, какие биты изменились, для меня это звучит довольно глупо, хотя стихи имеют 100% согласованное, независимое от платформы и полностью детерминированное решение, например: ENUM.
TL; DR: не слушайте ненавистников. C ++ не английский. Тот факт, что буквальное определение сокращенного ключевого слова, унаследованного от C, может не подходить для вашего использования, не означает, что вы не должны использовать его, когда определение ключевого слова на C и C ++ полностью включает ваш вариант использования. Вы также можете использовать структуры, чтобы моделировать вещи, отличные от структур, и классы для вещей, отличных от школьной и социальной касты. Вы можете использовать float для значений, которые обоснованы. Вы можете использовать char для переменных, которые не являются ни сожженными, ни людьми в романе, пьесе или фильме. Любой программист, который идет в словарь, чтобы определить значение ключевого слова до того, как спецификация языка ... ну, я буду там молчать.
Если вы хотите, чтобы ваш код был смоделирован на разговорном языке, лучше всего было бы писать в Objective-C, который, кстати, также активно использует перечисления для битовых полей.
[Flags]
атрибут работает просто отлично, т.е.:[Flags] enum class FlagBits{ Ready = 1, ReadMode = 2, WriteMode = 4, EOF = 8, Disabled = 16};