(¿Cómo) puedo contar los elementos en una enumeración?

98

Esta pregunta vino a mi mente, cuando tuve algo como

enum Folders {FA, FB, FC};

y quería crear una matriz de contenedores para cada carpeta:

ContainerClass*m_containers[3];
....
m_containers[FA] = ...; // etc.

(Usar mapas es mucho más elegante de usar: std::map<Folders, ContainerClass*> m_containers; )

Pero volviendo a mi pregunta original: ¿Qué pasa si no quiero codificar el tamaño de la matriz? ¿Hay alguna manera de averiguar cuántos elementos hay en las carpetas? (Sin depender, por ejemplo, de FCser el último elemento de la lista que permitiría algo como ContainerClass*m_containers[FC+1]si no me equivoco).

fuenfundachtzig
fuente
Esta publicación puede responder a su pregunta: stackoverflow.com/questions/1390703/enumerate-over-an-enum-in-c .
StackedCrooked el
1
La pregunta está un poco subespecificada. Según el estándar C ++, int(FA) | int(FB) | int (FC)también es un valor legal para una Foldersvariable. Si está dimensionando m_containerspara que cualquier Foldersvariable sea un índice válido, [FC+1]no sería lo suficientemente grande.
MSalters
Pregunté algo muy relacionado en stackoverflow.com/questions/12972317/count-on-enum-c-automatic
sergiol
Le recomiendo la solución de stackoverflow.com/a/60216003/12894563 y la variante mejorada de stackoverflow.com/a/60271408/12894563
ixjxk

Respuestas:

123

Realmente no hay una buena manera de hacer esto, generalmente ves un elemento adicional en la enumeración, es decir

enum foobar {foo, bar, baz, quz, FOOBAR_NR_ITEMS};

Entonces puedes hacer:

int fuz[FOOBAR_NR_ITEMS];

Aunque todavía no es muy agradable.

Pero, por supuesto, se da cuenta de que solo el número de elementos en una enumeración no es seguro, dado, por ejemplo,

enum foobar {foo, bar = 5, baz, quz = 20};

el número de elementos sería 4, pero los valores enteros de los valores de enumeración estarían fuera del rango del índice de la matriz. El uso de valores de enumeración para la indexación de matrices no es seguro, debe considerar otras opciones.

editar: según lo solicitado, hizo que la entrada especial sobresaliera más.

cuales
fuente
27
Llámalo LAST o ALWAYS_AT_END o algo no tan críptico. Haz que resalte . Para que los mantenedores posteriores no agreguen accidentalmente nuevas entradas después de que termine el marcador.
Martin York
3
Para bien o para mal, este es el enfoque que adoptamos en nuestra organización. Normalmente lo llamamos FINAL_enumname_ENTRY, como FINAL_foobar_ENTRY. También he visto a personas usar una variable const FOOBAR_COUNT estática separada definida justo después de la declaración enum, un enfoque que es un poco más propenso a errores.
Darryl
1
Al menos es bastante fácil ver que "enum foo {a = 10, LAST}" va a ser extraño. Y pensé que "int arr [LAST]" serían 11 elementos en este caso, no 2, por lo que la mayoría del código funcionará (pero está desperdiciando memoria en valores de índice no válidos)
Code Abominator
32

Para C ++, hay varias técnicas de enumeración con seguridad de tipos disponibles, y algunas de ellas (como el Boost.Enum propuesto pero nunca enviado ) incluyen soporte para obtener el tamaño de una enumeración.

El enfoque más simple, que funciona tanto en C como en C ++, es adoptar una convención para declarar un valor ... MAX para cada uno de sus tipos de enumeración:

enum Folders { FA, FB, FC, Folders_MAX = FC };
ContainerClass *m_containers[Folders_MAX + 1];
....
m_containers[FA] = ...; // etc.

Editar : Con respecto a { FA, FB, FC, Folders_MAX = FC}versus {FA, FB, FC, Folders_MAX]: prefiero establecer el valor ... MAX al último valor legal de la enumeración por algunas razones:

  1. El nombre de la constante es técnicamente más preciso (ya que Folders_MAXda el máximo valor de enumeración posible).
  2. Personalmente, me siento como Folders_MAX = FC destaca un poco más de otras entradas (lo que hace que sea un poco más difícil agregar accidentalmente valores de enumeración sin actualizar el valor máximo, un problema al que Martin York hizo referencia).
  3. GCC incluye advertencias útiles como "valor de enumeración no incluido en el conmutador" para códigos como el siguiente. Dejar Folders_MAX == FC + 1 rompe esas advertencias, ya que terminas con un montón de ... MAX valores de enumeración que nunca deberían incluirse en el cambio.
cambiar (carpeta) 
{
  caso FA: ...;
  caso FB: ...;
  // ¡Ups, me olvidé de FC!
}
Josh Kelley
fuente
3
¿Por qué no hacer enum Folders { FA, FB, FC, Folders_MAX }; ContainerClass *m_containers[Folders_MAX];:?
Factura
1
Prefiero dejar claro que el último es un número, y todos tienen el mismo nombre gracias a:struct SomeEnum { enum type {FA, FB, FC, NB__};};
Luc Hermitte
2
De hecho, siento que esas advertencias "útiles" son un verdadero dolor de cabeza. Me gustan las buenas advertencias que siempre configuro -Wall -pedantic, etc. cuando estoy desarrollando, pero estas advertencias son simplemente estúpidas. Solo hay algunos peores, como sugerir parens para && || y & ^ | precedencia del operador. Quiero decir, pensé que Java era el lenguaje de niñera, ¿qué diablos le está pasando a C y C ++ ...
que el
2
¡La desventaja de tener Folders_max = FC es que tienes que cambiarlo cada vez que agregas algo a la enumeración!
Étienne
2
enumeración Carpetas {FA, FB, FC, Folders_MIN = FA, Folders_MAX = FC}; ¿Solo para enfatizar que es útil para la iteración?
gjpc
7

¿Qué hay de los rasgos, en estilo STL? Por ejemplo:

enum Foo
{
    Bar,
    Baz
};

escribe un

std::numeric_limits<enum Foo>::max()

especialización (posiblemente constexpr si usa c ++ 11). Luego, en su código de prueba, proporcione cualquier aserción estática para mantener las restricciones que std :: numeric_limits :: max () = last_item.

Wojciech Migda
fuente
2
Desafortunadamente, esto no funcionará según esta respuesta .
rr-
3
std::numeric_limits<enum Foo>::max()siempre devuelve cero ... (vea la pregunta para la respuesta vinculada) Probado en enumeraciones normales ( enum Foo { ... }), enumeraciones enum class Foo : uint8_t { ... }con sugerencias de tipo ( ) con gcc 5.2.0 @ Linux y MinGW 4.9.3 @ Windows.
rr-
1
(... y en el caso de std::numeric_limits<std::underlying_type<Foo>::type>::max(), devuelve el valor máximo del tipo subyacente, es decir, 0xFFFFFFFF para enteros de 32 bits, que no es útil en este contexto.)
rr-
1
Extraño, porque tengo un código de trabajo que hace lo que describí. namespace gx { enum struct DnaNucleobase : char { A, C, G, T }; } Entonces: namespace std { template<> struct numeric_limits<enum ::gx::DnaNucleobase> { typedef enum ::gx::DnaNucleobase value_type; static constexpr value_type max() { return value_type::T; } (...) E std::cout << std::numeric_limits<::gx::DnaNucleobase>::max() << std::endl;imprime el resultado esperado. Probado con sabores gcc 5.2.1 y 4.8 / 4.9.
Wojciech Migda
2
-1; enviarme un ping si cambia la respuesta, para que pueda deshacer el voto negativo. Esta respuesta es una mala convención a seguir. Es un mal uso del concepto de numeric_limits<T>::max(). Lo único que esa función podría devolver razonablemente es el valor enumerado más alto. Volvería 2, pero el OP (en este caso específico) lo necesitaría para volver 3. Una vez que tenga valores no predeterminados para la enumeración ( FB = 2057), todas las apuestas están desactivadas, ni siquiera + 1puede esquivar el error de uno por uno. Si hubiera un numeric_limits<T>::number_of_elements_of_the_set()(o un nombre más corto), podría usarse sin ambigüedad.
Merlyn Morgan-Graham
3

Agregue una entrada, al final de su enumeración, llamada Folders_MAX o algo similar y use este valor al inicializar sus matrices.

ContainerClass* m_containers[Folders_MAX];
Kevin Doyon
fuente
2

Me gusta usar enumeraciones como argumentos para mis funciones. Es un medio sencillo para proporcionar una lista fija de "opciones". El problema con la respuesta más votada aquí es que al usarla, un cliente puede especificar una "opción no válida". Como un derivado, recomiendo hacer esencialmente lo mismo, pero usar un int constante fuera de la enumeración para definir el recuento de ellos.

enum foobar { foo, bar, baz, quz };
const int FOOBAR_NR_ITEMS=4;

No es agradable, pero es una solución limpia si no cambia la enumeración sin actualizar la constante.

BuvinJ
fuente
1

Realmente no veo ninguna forma de llegar realmente al número de valores en una enumeración en C ++. Cualquiera de las soluciones mencionadas anteriormente funciona siempre que no defina el valor de sus enumeraciones si define su valor que podría encontrarse en situaciones en las que crea matrices demasiado grandes o demasiado pequeñas

enum example{ test1 = -2, test2 = -1, test3 = 0, test4 = 1, test5 = 2 }

en esto sobre ejemplos, el resultado crearía una matriz de 3 elementos cuando necesite una matriz de 5 elementos

enum example2{ test1 , test2 , test3 , test4 , test5 = 301 }

en esto sobre ejemplos, el resultado crearía una matriz de 301 elementos cuando necesite una matriz de 5 elementos

La mejor manera de resolver este problema en el caso general sería iterar a través de sus enumeraciones, pero eso no está en el estándar hasta donde yo sé


fuente