Un enum X : int
(C #) o enum class X : int
(C ++ 11) es un tipo que tiene un campo interno oculto int
que puede contener cualquier valor. Además, una serie de constantes predefinidas de X
se definen en la enumeración. Es posible convertir la enumeración a su valor entero y viceversa. Todo esto es cierto tanto en C # como en C ++ 11.
En C #, las enumeraciones no solo se utilizan para mantener valores individuales, sino también para mantener combinaciones de marcas en bits, según la recomendación de Microsoft . Tales enumeraciones están (generalmente, pero no necesariamente) decoradas con el [Flags]
atributo. Para facilitar la vida de los desarrolladores, los operadores bit a bit (OR, AND, etc.) están sobrecargados para que pueda hacer fácilmente algo como esto (C #):
void M(NumericType flags);
M(NumericType.Sign | NumericType.ZeroPadding);
Soy un desarrollador experimentado de C #, pero he estado programando C ++ solo por un par de días y no conozco las convenciones de C ++. Tengo la intención de usar una enumeración de C ++ 11 de la misma manera que solía hacerlo en C #. En C ++ 11, los operadores bit a bit en las enumeraciones de ámbito no están sobrecargados, por lo que quería sobrecargarlos .
Esto solicitó un debate, y las opiniones parecen variar entre tres opciones:
Una variable del tipo enum se usa para mantener el campo de bits, similar a C #:
void M(NumericType flags); // With operator overloading: M(NumericType::Sign | NumericType::ZeroPadding); // Without operator overloading: M(static_cast<NumericType>(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding)));
Pero esto contrarrestaría la filosofía de enumeración fuertemente tipada de las enumeraciones de ámbito de C ++ 11.
Use un entero simple si desea almacenar una combinación de enumeraciones bit a bit:
void M(int flags); M(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding));
Pero esto reduciría todo a una
int
, dejándote sin idea de qué tipo se supone que debes poner en el método.Escriba una clase separada que sobrecargue a los operadores y mantenga las banderas bit a bit en un campo entero oculto:
class NumericTypeFlags { unsigned flags_; public: NumericTypeFlags () : flags_(0) {} NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {} //...define BITWISE test/set operations }; void M(NumericTypeFlags flags); M(NumericType::Sign | NumericType::ZeroPadding);
( código completo por usuario315052 )
Pero entonces no tienes IntelliSense o cualquier soporte que te sugiera los posibles valores.
Sé que esta es una pregunta subjetiva , pero: ¿qué enfoque debo usar? ¿Qué enfoque, si lo hay, es el más ampliamente reconocido en C ++? ¿Qué enfoque utiliza cuando trata con campos de bits y por qué ?
Por supuesto, dado que los tres enfoques funcionan, busco razones fácticas y técnicas, convenciones generalmente aceptadas y no simplemente preferencias personales.
Por ejemplo, debido a mi experiencia en C #, tiendo a utilizar el enfoque 1 en C ++. Esto tiene el beneficio adicional de que mi entorno de desarrollo puede darme pistas sobre los posibles valores, y con operadores de enumeración sobrecargados, esto es fácil de escribir y comprender, y bastante limpio. Y la firma del método muestra claramente qué tipo de valor espera. Pero la mayoría de la gente aquí no está de acuerdo conmigo, probablemente por una buena razón.
enum E { A = 1, B = 2, C = 4, };
, el rango es 0..7
(3 bits). Por lo tanto, el estándar C ++ garantiza explícitamente que # 1 siempre será una opción viable. [Específicamente, por enum class
defecto a enum class : int
menos que se especifique lo contrario, y por lo tanto siempre tiene un tipo subyacente fijo.])