(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.)



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 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.


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!
}

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.


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];

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.


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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.