c++ - uso - variable bandera c#
¿Cómo usar las enumeraciones como banderas en C++? (17)
¿Qué tipo es la variable seahawk.flags?
En C ++ estándar, las enumeraciones no son seguras para el tipo. Ellos son efectivamente enteros.
AnimalFlags NO debe ser el tipo de tu variable, tu variable debe ser int y el error desaparecerá.
No es necesario poner valores hexadecimales como otras personas, no hace ninguna diferencia.
Los valores enum SON de tipo int por defecto. Por lo tanto, seguramente puede bitwise O combinarlos y juntarlos y almacenar el resultado en un int.
El tipo enum es un subconjunto restringido de int cuyo valor es uno de sus valores enumerados. Por lo tanto, cuando crea un valor nuevo fuera de ese rango, no puede asignarlo sin convertirlo a una variable de su tipo enum.
También puede cambiar los tipos de valores enum si lo desea, pero no tiene sentido esta pregunta.
EDITAR: el póster dijo que estaban preocupados con la seguridad de tipo y que no quieren un valor que no debería existir dentro del tipo int.
Pero sería inseguro poner un valor fuera del rango de AnimalFlags dentro de una variable de tipo AnimalFlags.
Hay una manera segura de verificar valores fuera de rango aunque dentro del tipo int ...
int iFlags = HasClaws | CanFly;
//InvalidAnimalFlagMaxValue-1 gives you a value of all the bits
// smaller than itself set to 1
//This check makes sure that no other bits are set.
assert(iFlags & ~(InvalidAnimalFlagMaxValue-1) == 0);
enum AnimalFlags {
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8,
// put new enum values above here
InvalidAnimalFlagMaxValue = 16
};
Lo anterior no le impide poner un indicador no válido de una enumeración diferente que tiene el valor 1,2,4 u 8 embargo.
Si desea seguridad de tipo absoluta, puede simplemente crear un conjunto estándar y almacenar cada indicador allí. No es eficiente desde el punto de vista espacial, pero es seguro y te ofrece la misma capacidad que bitflag int.
C ++ 0x Nota: Enums fuertemente tipados
En C ++ 0x finalmente puedes tener valores de enum seguro.
enum class AnimalFlags {
CanFly = 2,
HasClaws = 4
};
if(CanFly == 2) { }//Compiling error
El tratamiento de enum
como indicadores funciona bien en C # a través del atributo [Flags]
, pero ¿cuál es la mejor manera de hacerlo en C ++?
Por ejemplo, me gustaría escribir:
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
seahawk.flags = CanFly | EatsFish | Endangered;
Sin embargo, obtengo errores del compilador con respecto a las conversiones int
/ enum
. ¿Hay alguna manera más agradable de expresar esto que solo el lanzamiento directo? Preferiblemente, no quiero depender de construcciones de bibliotecas de terceros como boost o Qt.
EDITAR: Como se indica en las respuestas, puedo evitar el error del compilador al declarar seahawk.flags
como int
. Sin embargo, me gustaría tener algún mecanismo para hacer cumplir la seguridad de tipo, por lo que alguien no puede escribir seahawk.flags = HasMaximizeButton
.
Aquí está mi solución sin necesidad de demasiados sobrecargas o castings:
namespace EFoobar
{
enum
{
FB_A = 0x1,
FB_B = 0x2,
FB_C = 0x4,
};
typedef long Flags;
}
void Foobar(EFoobar::Flags flags)
{
if (flags & EFoobar::FB_A)
// do sth
;
if (flags & EFoobar::FB_B)
// do sth
;
}
void ExampleUsage()
{
Foobar(EFoobar::FB_A | EFoobar::FB_B);
EFoobar::Flags otherflags = 0;
otherflags|= EFoobar::FB_B;
otherflags&= ~EFoobar::FB_B;
Foobar(otherflags);
}
Creo que está bien, porque identificamos enums y typets (no fuertemente tipados) de todos modos.
Como una nota lateral (más larga), si
- querer usar enumeraciones fuertemente tipadas y
- no necesita un poco de toquetear sus banderas
- el rendimiento no es un problema
Me gustaría llegar a esto:
#include <set>
enum class EFoobarFlags
{
FB_A = 1,
FB_B,
FB_C,
};
void Foobar(const std::set<EFoobarFlags>& flags)
{
if (flags.find(EFoobarFlags::FB_A) != flags.end())
// do sth
;
if (flags.find(EFoobarFlags::FB_B) != flags.end())
// do sth
;
}
void ExampleUsage()
{
Foobar({EFoobarFlags::FB_A, EFoobarFlags::FB_B});
std::set<EFoobarFlags> otherflags{};
otherflags.insert(EFoobarFlags::FB_B);
otherflags.erase(EFoobarFlags::FB_B);
Foobar(otherflags);
}
usando listas de inicializadores C ++ 11 y enum class
.
Aquí hay una opción para las máscaras de bits si no tiene un uso real para los valores enum individuales (por ejemplo, no necesita apagarlos) ... y si no está preocupado por mantener la compatibilidad binaria, es decir: no me importa dónde viven tus bits ... lo cual probablemente sea. Además, será mejor que no te preocupes demasiado por el alcance y el control de acceso. Hmmm, las enumeraciones tienen algunas buenas propiedades para los campos de bits ... se preguntan si alguien alguna vez lo ha intentado :)
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 :(";
}
}
Podemos ver que la vida es excelente, tenemos nuestros valores discretos, y tenemos una buena int para & y | para nuestro contenido de corazones, que todavía tiene un contexto de lo que significan sus bits. Todo es coherente y predecible ... para mí ... siempre que siga usando el compilador de VC ++ de Microsoft con la Actualización 3 en Win10 x64 y no toque mis indicadores de compilación :)
Aunque todo es genial ... tenemos un contexto sobre el significado de las banderas ahora, ya que está en una unión con el campo de bits en el terrible mundo real donde tu programa puede ser responsable de más de una tarea discreta que podrías hacer. todavía accidentalmente (con bastante facilidad) destrozar dos campos de banderas de diferentes uniones (por ejemplo, AnimalProperties y ObjectProperties, ya que ambos son enteros), mezclando todos los bits de tu propiedad, lo que es un error horrible para rastrear ... y cómo sé Mucha gente en esta publicación no trabaja con máscaras de bits muy a menudo, ya que construirlas es fácil y mantenerlas es difícil.
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 :(
}
Entonces, usted declara privada su declaración sindical para evitar el acceso directo a "Banderas", y tiene que agregar getters / setters y sobrecargas de operador, luego crear una macro para todo eso, y básicamente está justo donde comenzó cuando intentó haz esto con un Enum.
Desafortunadamente, si quiere que su código sea portátil, no creo que haya ninguna forma de A) garantizar el diseño de bits o B) determinar el diseño de bits en tiempo de compilación (para que pueda rastrearlo y al menos corregir los cambios en versiones / plataformas, etc.) Desplazamiento en una estructura con campos de bits
En tiempo de ejecución puedes jugar trucos con los campos y XORing las banderas para ver qué bits cambiaron, suena bastante malo para mí, aunque los versos tienen una solución 100% consistente, independiente de la plataforma y completamente determinística, es decir: un ENUM.
TL; DR: No escuches a los enemigos. C ++ no es inglés. El hecho de que la definición literal de una palabra clave abreviada heredada de C no se ajuste a su uso no significa que no deba usarla cuando la definición de C y C ++ de la palabra clave incluye su caso de uso. También puede usar structs para modelar cosas que no sean estructuras y clases para cosas que no sean la escuela y la casta social. Puede usar float para valores que están fundamentados. Puede usar caracteres para las variables que no son ni quemadas ni una persona en una novela, obra o película. Cualquier programador que vaya al diccionario para determinar el significado de una palabra clave antes de la especificación del idioma es ... bueno, me quedo con la lengua allí.
Si quieres que tu código se modele después del lenguaje hablado, lo mejor será que escribas en Objective-C, que por cierto también usa enumeraciones en gran medida para bitfields.
Como arriba (Kai) o haga lo siguiente. Realmente las enumeraciones son "enumeraciones", lo que quieres hacer es tener un conjunto, por lo tanto, realmente deberías usar stl :: set
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
int main(void)
{
AnimalFlags seahawk;
//seahawk= CanFly | EatsFish | Endangered;
seahawk= static_cast<AnimalFlags>(CanFly | EatsFish | Endangered);
}
Considero que la respuesta actualmente aceptada por eidolon es demasiado peligrosa. El optimizador del compilador puede hacer suposiciones sobre los valores posibles en la enumeración y puede recuperar la basura con valores no válidos. Y, por lo general, nadie quiere definir todas las permutaciones posibles en las enumeraciones de banderas.
Como dice Brian R. Bondy a continuación, si estás usando C ++ 11 (que todos deberían, es así de bueno) ahora puedes hacerlo más fácilmente con la enum class
:
enum class ObjectType : uint32_t
{
ANIMAL = (1 << 0),
VEGETABLE = (1 << 1),
MINERAL = (1 << 2)
};
constexpr enum ObjectType operator |( const enum ObjectType selfValue, const enum ObjectType inValue )
{
return (enum ObjectType)(uint32_t(selfValue) | uint32_t(inValue));
}
// ... add more operators here.
Esto garantiza un tamaño estable y un rango de valores al especificar un tipo para la enumeración, inhibe la bajada automática de enumeraciones a etc. utilizando enum class
, y utiliza constexpr
para asegurar que el código para los operadores se inserte y por lo tanto tan rápido como los números regulares .
Para personas atrapadas con dialectos C ++ pre-11
Si estuviera atascado con un compilador que no es compatible con C ++ 11, me gustaría incluir un int-type en una clase que luego solo permita el uso de operadores bit a bit y los tipos de esa enumeración para establecer sus valores:
template<class ENUM,class UNDERLYING=typename std::underlying_type<ENUM>::type>
class SafeEnum
{
public:
SafeEnum() : mFlags(0) {}
SafeEnum( ENUM singleFlag ) : mFlags(singleFlag) {}
SafeEnum( const SafeEnum& original ) : mFlags(original.mFlags) {}
SafeEnum& operator |=( ENUM addValue ) { mFlags |= addValue; return *this; }
SafeEnum operator |( ENUM addValue ) { SafeEnum result(*this); result |= addValue; return result; }
SafeEnum& operator &=( ENUM maskValue ) { mFlags &= maskValue; return *this; }
SafeEnum operator &( ENUM maskValue ) { SafeEnum result(*this); result &= maskValue; return result; }
SafeEnum operator ~() { SafeEnum result(*this); result.mFlags = ~result.mFlags; return result; }
explicit operator bool() { return mFlags != 0; }
protected:
UNDERLYING mFlags;
};
Puede definir esto más o menos como un enum regular typedef:
enum TFlags_
{
EFlagsNone = 0,
EFlagOne = (1 << 0),
EFlagTwo = (1 << 1),
EFlagThree = (1 << 2),
EFlagFour = (1 << 3)
};
typedef SafeEnum<enum TFlags_> TFlags;
Y el uso es similar también:
TFlags myFlags;
myFlags |= EFlagTwo;
myFlags |= EFlagThree;
if( myFlags & EFlagTwo )
std::cout << "flag 2 is set" << std::endl;
if( (myFlags & EFlagFour) == EFlagsNone )
std::cout << "flag 4 is not set" << std::endl;
Y también puede anular el tipo subyacente para las enumeraciones binarias-estables (como enum foo : type
C ++ 11) utilizando el segundo parámetro de plantilla, es decir, typedef SafeEnum<enum TFlags_,uint8_t> TFlags;
.
operator bool
anulación de operator bool
del operator bool
con la palabra clave explicit
C ++ 11 para evitar que se produzcan conversiones int, ya que podrían hacer que los conjuntos de indicadores colapsen en 0 o 1 al escribirlos. Si no puede usar C ++ 11, deje esa sobrecarga y (myFlags & EFlagTwo) == EFlagTwo
escribir el primer condicional en el uso del ejemplo como (myFlags & EFlagTwo) == EFlagTwo
.
El estándar C ++ habla explícitamente sobre esto, consulte la sección "17.5.2.1.3 Tipos de máscara de bits":
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf
Dada esta "plantilla", obtienes:
enum AnimalFlags : unsigned int
{
HasClaws = 1,
CanFly = 2,
EatsFish = 4,
Endangered = 8
};
constexpr AnimalFlags operator|(AnimalFlags X, AnimalFlags Y) {
return static_cast<AnimalFlags>(
static_cast<unsigned int>(X) | static_cast<unsigned int>(Y));
}
AnimalFlags& operator|=(AnimalFlags& X, AnimalFlags Y) {
X = X | Y; return X;
}
Y similar para los otros operadores. También tenga en cuenta el "constexpr", es necesario si desea que el compilador pueda ejecutar el tiempo de compilación de los operadores.
Si está utilizando C ++ / CLI y desea poder asignar a los miembros enum de las clases ref, debe usar referencias de seguimiento en su lugar:
AnimalFlags% operator|=(AnimalFlags% X, AnimalFlags Y) {
X = X | Y; return X;
}
NOTA: Esta muestra no está completa; consulte la sección "17.5.2.1.3 Tipos de máscara de bits" para obtener un conjunto completo de operadores.
En mi opinión, ninguna de las respuestas hasta ahora es ideal. Para ser ideal, esperaría la solución:
- Soporta el
==
!=
,=
,&
,&=
,|
,|=
y~
operadores en el sentido convencional (es decir,a & b
) - Sea seguro de tipo, es decir, no permita que se asignen valores no enumerados, como literales o tipos enteros (excepto para las combinaciones bit a bit de valores enumerados) o permita que una variable enum sea asignada a un tipo de entero
- Expresiones de permisos como
if (a & b)...
- No requiere macros malvadas, características específicas de implementación u otros hacks
La mayoría de las soluciones hasta ahora caen en los puntos 2 o 3. WebDancer es el cierre, en mi opinión, pero falla en el punto 3 y debe repetirse para cada enumeración.
Mi solución propuesta es una versión generalizada de WebDancer que también aborda el punto 3:
#include <cstdint>
#include <type_traits>
template<typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
class auto_bool
{
T val_;
public:
constexpr auto_bool(T val) : val_(val) {}
constexpr operator T() const { return val_; }
constexpr explicit operator bool() const
{
return static_cast<std::underlying_type_t<T>>(val_) != 0;
}
};
template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
constexpr auto_bool<T> operator&(T lhs, T rhs)
{
return static_cast<T>(
static_cast<typename std::underlying_type<T>::type>(lhs) &
static_cast<typename std::underlying_type<T>::type>(rhs));
}
template <typename T = typename std::enable_if<std::is_enum<T>::value, T>::type>
constexpr T operator|(T lhs, T rhs)
{
return static_cast<T>(
static_cast<typename std::underlying_type<T>::type>(lhs) |
static_cast<typename std::underlying_type<T>::type>(rhs));
}
enum class AnimalFlags : uint8_t
{
HasClaws = 1,
CanFly = 2,
EatsFish = 4,
Endangered = 8
};
enum class PlantFlags : uint8_t
{
HasLeaves = 1,
HasFlowers = 2,
HasFruit = 4,
HasThorns = 8
};
int main()
{
AnimalFlags seahawk = AnimalFlags::CanFly; // Compiles, as expected
AnimalFlags lion = AnimalFlags::HasClaws; // Compiles, as expected
PlantFlags rose = PlantFlags::HasFlowers; // Compiles, as expected
// rose = 1; // Won''t compile, as expected
if (seahawk != lion) {} // Compiles, as expected
// if (seahawk == rose) {} // Won''t compile, as expected
// seahawk = PlantFlags::HasThorns; // Won''t compile, as expected
seahawk = seahawk | AnimalFlags::EatsFish; // Compiles, as expected
lion = AnimalFlags::HasClaws | // Compiles, as expected
AnimalFlags::Endangered;
// int eagle = AnimalFlags::CanFly | // Won''t compile, as expected
// AnimalFlags::HasClaws;
// int has_claws = seahawk & AnimalFlags::CanFly; // Won''t compile, as expected
if (seahawk & AnimalFlags::CanFly) {} // Compiles, as expected
seahawk = seahawk & AnimalFlags::CanFly; // Compiles, as expected
return 0;
}
Esto crea sobrecargas de los operadores necesarios pero usa SFINAE para limitarlos a los tipos enumerados. Tenga en cuenta que, en aras de la brevedad, no he definido todos los operadores, pero el único que es diferente es el &
. Los operadores son actualmente globales (es decir, se aplican a todos los tipos enumerados) pero esto podría reducirse colocando las sobrecargas en un espacio de nombres (lo que hago), o agregando condiciones SFINAE adicionales (tal vez utilizando tipos subyacentes particulares o alias de tipo creados especialmente) ) The underlying_type_t
es una característica de C ++ 14 pero parece estar bien soportado y es fácil de emular para C ++ 11 con una template<typename T> using underlying_type_t = underlying_type<T>::type;
simple template<typename T> using underlying_type_t = underlying_type<T>::type;
Estás confundiendo objetos y colecciones de objetos. Específicamente, está confundiendo banderas binarias con conjuntos de banderas binarias. Una solución adecuada se vería así:
// These are individual flags
enum AnimalFlag // Flag, not Flags
{
HasClaws = 0,
CanFly,
EatsFish,
Endangered
};
class AnimalFlagSet
{
int m_Flags;
public:
AnimalFlagSet() : m_Flags(0) { }
void Set( AnimalFlag flag ) { m_Flags |= (1 << flag); }
void Clear( AnimalFlag flag ) { m_Flags &= ~ (1 << flag); }
bool Get( AnimalFlag flag ) const { return (m_Flags >> flag) & 1; }
};
La forma "correcta" es definir operadores de bits para la enumeración, como:
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
inline AnimalFlags operator|(AnimalFlags a, AnimalFlags b)
{return static_cast<AnimalFlags>(static_cast<int>(a) | static_cast<int>(b));}
Etc. resto de los operadores de bit. Modifique según sea necesario si el rango enum excede el rango int.
La forma más fácil de hacerlo es como se muestra here , utilizando el conjunto de bitset clase de biblioteca estándar.
Para emular la característica C # de una manera segura para el tipo, tendría que escribir un contenedor de plantilla alrededor del conjunto de bits, reemplazando los argumentos int con una enumeración dada como parámetro de tipo para la plantilla. Algo como:
template <class T, int N>
class FlagSet
{
bitset<N> bits;
FlagSet(T enumVal)
{
bits.set(enumVal);
}
// etc.
};
enum MyFlags
{
FLAG_ONE,
FLAG_TWO
};
FlagSet<MyFlags, 2> myFlag;
Me encontré haciendo la misma pregunta y se me ocurrió una solución genérica basada en C ++ 11, similar a la de soru:
template <typename TENUM>
class FlagSet {
private:
using TUNDER = typename std::underlying_type<TENUM>::type;
std::bitset<std::numeric_limits<TUNDER>::max()> m_flags;
public:
FlagSet() = default;
template <typename... ARGS>
FlagSet(TENUM f, ARGS... args) : FlagSet(args...)
{
set(f);
}
FlagSet& set(TENUM f)
{
m_flags.set(static_cast<TUNDER>(f));
return *this;
}
bool test(TENUM f)
{
return m_flags.test(static_cast<TUNDER>(f));
}
FlagSet& operator|=(TENUM f)
{
return set(f);
}
};
La interfaz se puede mejorar a gusto. Entonces se puede usar así:
FlagSet<Flags> flags{Flags::FLAG_A, Flags::FLAG_C};
flags |= Flags::FLAG_D;
Me gustaría profundizar en la respuesta de Uliwitness , corregir su código para C ++ 98 y usar el modismo de Safe Bool , por falta de la plantilla std::underlying_type<>
y la palabra clave explicit
en las versiones de C ++ por debajo de C ++ 11.
También lo modifiqué para que los valores enum puedan ser secuenciales sin ninguna asignación explícita, por lo que puede tener
enum AnimalFlags_
{
HasClaws,
CanFly,
EatsFish,
Endangered
};
typedef FlagsEnum<AnimalFlags_> AnimalFlags;
seahawk.flags = AnimalFlags() | CanFly | EatsFish | Endangered;
A continuación, puede obtener el valor de indicadores crudos con
seahawk.flags.value();
Aquí está el código.
template <typename EnumType, typename Underlying = int>
class FlagsEnum
{
typedef Underlying FlagsEnum::* RestrictedBool;
public:
FlagsEnum() : m_flags(Underlying()) {}
FlagsEnum(EnumType singleFlag):
m_flags(1 << singleFlag)
{}
FlagsEnum(const FlagsEnum& original):
m_flags(original.m_flags)
{}
FlagsEnum& operator |=(const FlagsEnum& f) {
m_flags |= f.m_flags;
return *this;
}
FlagsEnum& operator &=(const FlagsEnum& f) {
m_flags &= f.m_flags;
return *this;
}
friend FlagsEnum operator |(const FlagsEnum& f1, const FlagsEnum& f2) {
return FlagsEnum(f1) |= f2;
}
friend FlagsEnum operator &(const FlagsEnum& f1, const FlagsEnum& f2) {
return FlagsEnum(f1) &= f2;
}
FlagsEnum operator ~() const {
FlagsEnum result(*this);
result.m_flags = ~result.m_flags;
return result;
}
operator RestrictedBool() const {
return m_flags ? &FlagsEnum::m_flags : 0;
}
Underlying value() const {
return m_flags;
}
protected:
Underlying m_flags;
};
Nota (también un poco fuera de tema): Otra forma de hacer banderas únicas se puede hacer usando un cambio de bit. Yo, yo mismo, encuentro esto más fácil de leer.
enum Flags
{
A = 1 << 0, // binary 0001
B = 1 << 1, // binary 0010
C = 1 << 2, // binary 0100
D = 1 << 3, // binary 1000
};
Puede contener valores hasta un int así que, la mayoría de las veces, 32 banderas que se reflejan claramente en la cantidad de desplazamiento.
Para gente perezosa como yo, aquí hay una solución para copiar y pegar:
template<class T> inline T operator~ (T a) { return (T)~(int)a; }
template<class T> inline T operator| (T a, T b) { return (T)((int)a | (int)b); }
template<class T> inline T operator& (T a, T b) { return (T)((int)a & (int)b); }
template<class T> inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); }
template<class T> inline T& operator|= (T& a, T b) { return (T&)((int&)a |= (int)b); }
template<class T> inline T& operator&= (T& a, T b) { return (T&)((int&)a &= (int)b); }
template<class T> inline T& operator^= (T& a, T b) { return (T&)((int&)a ^= (int)b); }
Si su compilador aún no admite enums fuertemente tipados, puede echarle un vistazo al siguiente artículo de la fuente de c ++:
Del resumen:
Este artículo presenta una solución al problema de restringir las operaciones de bits a
permitir solo los seguros y legítimos, y convertir todas las manipulaciones de bits inválidas en errores de tiempo de compilación. Lo mejor de todo es que la sintaxis de las operaciones de bits permanece inalterada, y no es necesario modificar el código que trabaja con bits, excepto posiblemente para corregir errores que aún no se han detectado.
Solo azúcar sintáctico. Sin metadatos adicionales
namespace UserRole // grupy
{
constexpr uint8_t dea = 1;
constexpr uint8_t red = 2;
constexpr uint8_t stu = 4;
constexpr uint8_t kie = 8;
constexpr uint8_t adm = 16;
constexpr uint8_t mas = 32;
}
Los operadores de bandera en tipo integral simplemente funcionan.
Tenga en cuenta que si está trabajando en un entorno Windows, hay una macro DEFINE_ENUM_FLAG_OPERATORS
definida en winnt.h que hace el trabajo por usted. Entonces, en este caso, puedes hacer esto:
enum AnimalFlags
{
HasClaws = 1,
CanFly =2,
EatsFish = 4,
Endangered = 8
};
DEFINE_ENUM_FLAG_OPERATORS(AnimalFlags)
seahawk.flags = CanFly | EatsFish | Endangered;