(Como) posso contar os itens em um enum?

98

Esta questão veio à minha mente, quando eu tinha algo como

enum Folders {FA, FB, FC};

e queria criar uma matriz de contêineres para cada pasta:

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

(Usar mapas é muito mais elegante de usar: std::map<Folders, ContainerClass*> m_containers; :)

Mas, voltando à minha pergunta original: e se eu não quiser codificar o tamanho do array, há uma maneira de descobrir quantos itens estão nas pastas? (Sem depender de, por exemplo, FCser o último item da lista o que permitiria algo como ContainerClass*m_containers[FC+1]se não estou enganado.)

Fuenfundachtzig
fonte
Esta postagem pode responder à sua pergunta: stackoverflow.com/questions/1390703/enumerate-over-an-enum-in-c .
StackedCrooked de
1
A questão está um pouco subespecificada. De acordo com o padrão C ++, int(FA) | int(FB) | int (FC)também é um valor legal para uma Foldersvariável. Se você estiver dimensionando de m_containersforma que qualquer Foldersvariável seja um índice válido, [FC+1]não seria grande o suficiente.
MSalters
Eu perguntei algo muito relacionado em stackoverflow.com/questions/12972317/count-on-enum-c-automatic
sergiol
Eu recomendo a você a solução de stackoverflow.com/a/60216003/12894563 e a variante aprimorada de stackoverflow.com/a/60271408/12894563
ixjxk

Respostas:

123

Não há realmente uma boa maneira de fazer isso, geralmente você vê um item extra no enum, ou seja,

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

Então você pode fazer:

int fuz[FOOBAR_NR_ITEMS];

Ainda não é muito bom.

Mas é claro que você percebe que apenas o número de itens em um enum não é seguro, dado, por exemplo,

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

o número de itens seria 4, mas os valores inteiros dos valores enum estariam fora do intervalo do índice da matriz. Usar valores enum para indexação de array não é seguro, você deve considerar outras opções.

editar: conforme solicitado, fez a entrada especial destacar-se mais.

qual
fonte
27
Chame-o de LAST ou ALWAYS_AT_END ou algo não tão enigmático. Faça isso ficar para fora . Para que os mantenedores subsequentes não adicionem acidentalmente novas entradas após o término do marcador.
Martin York
3
Para melhor ou pior, esta é a abordagem que adotamos em nossa organização. Normalmente o chamamos de FINAL_enumname_ENTRY, como FINAL_foobar_ENTRY. Também vi pessoas usarem uma variável const FOOBAR_COUNT separada estática definida logo após a declaração enum, uma abordagem que é um pouco mais sujeita a erros.
Darryl de
1
Pelo menos é bastante fácil ver que "enum foo {a = 10, LAST}" vai ser estranho. E eu pensei que "int arr [LAST]" seria 11 itens neste caso, não 2, então a maior parte do código funcionará (mas você está desperdiçando memória em valores de índice inválidos)
Código Abominador
32

Para C ++, existem várias técnicas de enum de tipo seguro disponíveis, e algumas delas (como o Boost.Enum proposto-mas-nunca-enviado ) incluem suporte para obter o tamanho de um enum.

A abordagem mais simples, que funciona tanto em C quanto em C ++, é adotar uma convenção de declarar um valor ... MAX para cada um de seus tipos de enum:

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

Editar : Quanto { FA, FB, FC, Folders_MAX = FC}contra {FA, FB, FC, Folders_MAX]: Eu prefiro definir o ... valor MAX para o último valor legal do enum por algumas razões:

  1. O nome da constante é tecnicamente mais preciso (uma vez que Folders_MAXfornece o valor enum máximo possível).
  2. Pessoalmente, sinto que Folders_MAX = FC se destaca um pouco mais das outras entradas (tornando um pouco mais difícil adicionar acidentalmente valores enum sem atualizar o valor máximo, um problema mencionado por Martin York).
  3. O GCC inclui avisos úteis como "valor de enumeração não incluído na opção" para códigos como o seguinte. Permitir que Folders_MAX == FC + 1 quebra esses avisos, uma vez que você acaba com um monte de ... valores de enumeração MAX que nunca devem ser incluídos na opção.
mudar (pasta) 
{
  caso FA: ...;
  caso FB: ...;
  // Ops, esqueci o FC!
}
Josh Kelley
fonte
3
Por que não fazer enum Folders { FA, FB, FC, Folders_MAX }; ContainerClass *m_containers[Folders_MAX];:?
Bill de
1
Prefiro deixar claro que o último é um número, e todos têm o mesmo nome graças a:struct SomeEnum { enum type {FA, FB, FC, NB__};};
Luc Hermitte 21.01.10
2
Na verdade, eu sinto que esses avisos "úteis" são um pé no saco. Gosto de bons avisos que sempre defino -Wall -pedantic, etc. quando estou desenvolvendo, mas esses avisos são simplesmente estúpidos. Existem apenas alguns piores, como sugerir parênteses para && || e & ^ | operador precedente. Quer dizer, eu pensei que java fosse a linguagem de babá, o que diabos está acontecendo com C e C ++ ...
que dia
2
A desvantagem de ter Folders_max = FC é que você tem que alterá-lo sempre que adicionar algo ao enum!
Étienne
2
enum Folders {FA, FB, FC, Folders_MIN = FA, Folders_MAX = FC}; Apenas para enfatizar que é útil para iteração?
gjpc
7

Que tal traços, em estilo STL? Por exemplo:

enum Foo
{
    Bar,
    Baz
};

escrever um

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

especialização (possivelmente constexpr se você usar c ++ 11). Então, em seu código de teste forneça quaisquer asserções estáticas para manter as restrições que std :: numeric_limits :: max () = last_item.

Wojciech Migda
fonte
2
Infelizmente, isso não funcionará de acordo com esta resposta .
rr-
3
std::numeric_limits<enum Foo>::max()sempre retorna zero ... (consulte a pergunta para obter a resposta vinculada) Testado em enums normais ( enum Foo { ... }), enums enum class Foo : uint8_t { ... }com dicas de tipo ( ) com gcc 5.2.0 @ Linux e MinGW 4.9.3 @ Windows.
rr-
1
(... e no caso de std::numeric_limits<std::underlying_type<Foo>::type>::max(), ele retorna o valor máximo do tipo subjacente, ou seja, 0xFFFFFFFF para inteiros de 32 bits, o que não é útil neste contexto.)
rr-
1
Estranho, porque tenho um código de trabalho que faz o que descrevi. namespace gx { enum struct DnaNucleobase : char { A, C, G, T }; } Então: 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 o resultado esperado. Testado com os sabores gcc 5.2.1 e 4.8 / 4.9.
Wojciech Migda
2
-1; Envie um ping se você alterar a resposta, para que eu possa desfazer a votação negativa. Esta resposta é uma convenção ruim a seguir. É um mau uso do conceito de numeric_limits<T>::max(). A única coisa que a função poderia retornar razoavelmente é o valor enumerado mais alto. Ele retornaria 2, mas o OP (neste caso específico) precisaria que ele retornasse 3. Uma vez que você tenha valores não padrão para o enum ( FB = 2057), todas as apostas estão canceladas, não é possível nem mesmo + 1contornar o erro off-by-one. Se houvesse um numeric_limits<T>::number_of_elements_of_the_set()(ou um nome mais curto), ele poderia ser usado sem ambigüidade.
Merlyn Morgan-Graham
3

Adicione uma entrada, no final de seu enum, chamada Folders_MAX ou algo semelhante e use esse valor ao inicializar seus arrays.

ContainerClass* m_containers[Folders_MAX];
Kevin doyon
fonte
2

Gosto de usar enums como argumentos para minhas funções. É um meio fácil de fornecer uma lista fixa de "opções". O problema com a resposta mais votada aqui é que, usando-a, um cliente pode especificar uma "opção inválida". Como um spin off, recomendo fazer essencialmente a mesma coisa, mas usar um int constante fora do enum para definir a contagem deles.

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

Não é agradável, mas é uma solução limpa se você não alterar o enum sem atualizar a constante.

BuvinJ
fonte
1

Eu realmente não vejo nenhuma maneira de realmente obter o número de valores em uma enumeração em C ++. Qualquer uma das soluções antes mencionadas funciona, desde que você não defina o valor de suas enumerações se você definir seu valor que poderá se deparar com situações em que criará matrizes muito grandes ou muito pequenas

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

neste exemplo, o resultado criaria um array de 3 itens quando você precisa de um array de 5 itens

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

neste exemplo, o resultado criaria uma matriz de 301 itens quando você precisa de uma matriz de 5 itens

A melhor maneira de resolver este problema no caso geral seria iterar por meio de suas enumerações, mas isso não está no padrão até onde eu sei


fonte