Os itens vinculados têm algumas respostas interessantes.
Tony
Essas respostas parecem não cobrir o problema que intpode não ser grande o suficiente! ( [C++03: 7.2/5])
Lightness Races in Orbit
Curiosamente, você pode definir operator++enums; no entanto, para que você possa fazer for(Enum_E e = (Enum_E)0; e < ENUM_COUNT; e++). Observe que você precisa converter 0para Enum_Eporque o C ++ proíbe operadores de atribuição em enumerações.
weberc2
Se houvesse um operador de tempo de compilação, semelhante à maneira como sizeof funciona, que pudesse emitir um literal std :: initializer_list composto pelos valores da enumeração, teríamos uma solução e não envolveríamos nenhuma sobrecarga de tempo de execução.
Observe que o enum Lastdeve ser ignorado pela iteração. Utilizando essa Lastenumeração "falsa" , você não precisa atualizar sua condição final no loop for para a última enumeração "real" toda vez que desejar adicionar uma nova enumeração. Se você quiser adicionar mais enumerações posteriormente, basta adicioná-las antes do Último. O loop neste exemplo ainda funcionará.
Obviamente, isso será interrompido se os valores de enumeração forem especificados:
enumFoo{One=1,Two=9,Three=4,Last};
Isso ilustra que um enum não é realmente destinado a iterar. A maneira típica de lidar com uma enumeração é usá-la em uma instrução switch.
Se você realmente deseja enumerar, encha os valores de enum em um vetor e itere sobre ele. Isso também lidará adequadamente com os valores de enum especificados.
Observe que, na primeira parte do exemplo, se você quiser usar 'i' como um eno Foo e não como um int, precisará convertê-lo estático como: static_cast <Foo> (i)
Clayton
5
Além disso, você está pulando Last in the loop. Deve ser <= Último
Tony
20
@ Tony Last é para ser pulado. Se você quiser adicionar mais enums posteriormente, adicione-as antes de Last ... o loop no primeiro exemplo ainda funcionará. Ao utilizar uma última enumeração "falsa", você não precisa atualizar sua condição de encerramento no loop for para a última enumeração "real" toda vez que desejar adicionar uma nova enumeração.
precisa saber é o seguinte
Exceto agora que você realmente alocou memória quando um enum, desde que seja indexado a zero e estritamente contínuo, possa executar essa tarefa sem alocar memória.
Cloud
1
Observe que, para que essa definição de enum seja segura para atualizações, deve-se definir um valor UNKNOWN = 0. Além disso, eu sugeriria apenas descartar o defaultcaso ao alternar valores de enum, pois ele pode ocultar casos em que o tratamento de valores foi esquecido até o tempo de execução. Em vez disso, deve-se codificar todos os valores e usar o UNKNOWNcampo para detectar incompatibilidades.
22717 Benjamin Bannier
53
#include<iostream>#include<algorithm>namespaceMyEnum{enumType{
a =100,
b =220,
c =-1};staticconstTypeAll[]={ a, b, c };}void fun(constMyEnum::Type e ){
std::cout << e << std::endl;}int main(){// allfor(constauto e :MyEnum::All)
fun( e );// somefor(constauto e :{MyEnum::a,MyEnum::b })
fun( e );// all
std::for_each( std::begin(MyEnum::All), std::end(MyEnum::All), fun );return0;}
Obrigado! Observe que, se você estiver cruzando arquivos / classes e se a compatibilidade do MS estiver fornecendo problemas com constantes não inteiras declaradas pelo cabeçalho, o meu compilador ajudará no explicitamente colocar o tamanho do tipo no cabeçalho: static const Type All[3];e então eu posso para inicializar na fonte: const MyEnum::Type MyEnum::All[3] = { a, b, c }; Antes de fazer isso, eu estava recebendo Error in range-based for...erros desagradáveis (porque a matriz tinha um tamanho desconhecido). Descobriram isso graças a uma resposta relacionada
sage
1
A versão do array é muito amigável para copiar e colar. A resposta mais satisfatória além disso, "NÃO" ou "apenas para sequencial". Provavelmente macro amigável mesmo.
Paulo Neves
1
essa pode ser uma boa solução para enumerações com pequeno número de itens, mas para enumerações com grande número de itens, ela não deve se encaixar bem.
kato2
20
Se o seu enum começa com 0 e o incremento é sempre 1.
Com o c ++ 11, na verdade existe uma alternativa: escrever um iterador personalizado simples e com modelo.
vamos assumir que seu enum é
enumclass foo {
one,
two,
three
};
Esse código genérico fará o truque, com bastante eficiência - coloque em um cabeçalho genérico, servirá para qualquer enumeração que você precise repetir:
#include<type_traits>template<typename C, C beginVal, C endVal>classIterator{typedeftypename std::underlying_type<C>::type val_t;int val;public:Iterator(const C & f): val(static_cast<val_t>(f)){}Iterator(): val(static_cast<val_t>(beginVal)){}Iteratoroperator++(){++val;return*this;}
C operator*(){returnstatic_cast<C>(val);}Iterator begin(){return*this;}//default ctor is goodIterator end(){staticconstIterator endIter=++Iterator(endVal);// cache itreturn endIter;}booloperator!=(constIterator& i){return val != i.val;}};
for(foo i : fooIterator()){//notice the parentheses!
do_stuff(i);}
A suposição de que você não tem lacunas na enumeração ainda é verdadeira; não há suposição sobre o número de bits realmente necessário para armazenar o valor da enumeração (graças a std :: subjac_type)
Não sei por que isso foi prejudicado. É uma solução razoável.
Paul Brannan
11
Espero que seja porque as entradas precisam ser mantidas em dois lugares.
Ant
O C ++ permite a for (NodePosition pos : NodePositionVector)sintaxe? Tanto quanto sei, essa é a sintaxe Java, e você precisará de iteradores em C ++ para fazer algo equivalente.
precisa
3
@thegreatjedi Desde C ++ 11 você pode, mesmo simpier: para (auto pos: NodePositionVector) {..}
Enzojz
@thegreatjedi Teria sido mais rápido pesquisar, ou até compilar um programa de teste, do que fazer essa pergunta. Mas sim, desde o C ++ 11, é perfeitamente válido a sintaxe do C ++, que o compilador converte no código equivalente (e muito mais detalhado / menos abstrato), geralmente por meio de iteradores; veja cppreference . E, como Enzojz disse, o C ++ 11 também adicionou auto, assim você não precisa declarar explicitamente o tipo dos elementos, a menos que você (A) precise usar um operador de conversão ou (B) não goste autopor algum motivo . A maioria gama- forusuários usam autoAFAICT
underscore_d
9
Costumo fazer assim
enumEMyEnum{
E_First,
E_Orange = E_First,
E_Green,
E_White,
E_Blue,
E_Last
}for(EMyEnum i = E_First; i < E_Last; i =EMyEnum(i +1)){}
ou se não for sucessivo, mas com etapa regular (por exemplo, sinalizadores de bit)
mas, qual é a utilidade de imprimir os valores em bits?
Anu
1
Para usar enumerações para criar máscaras de bits. por exemplo, combine várias opções em uma única variável e use o FOR para testar todas as opções. Corrigido meu post com um exemplo melhor.
Niki
Ainda não consigo usá-lo (e sua postagem ainda mostra o exemplo antigo)! Usar enum como máscaras de bits é realmente útil, mas não conseguiu conectar os pontos! Se você puder elaborar um pouco mais o exemplo em detalhes, também poderá colocar o código adicional.
Anu
@anu Desculpe não viu seu comentário. Adicionada classe Frog como exemplo de máscara de bits
Niki
8
Você não pode com um enum. Talvez um enum não seja o mais adequado para sua situação.
Uma convenção comum é nomear o último valor da enumeração como MAX e usá-lo para controlar um loop usando um int.
Existem vários exemplos aqui que demonstram o contrário. Na sua própria declaração, você está se contradizendo (segunda linha).
Niki
6
Algo que não foi abordado nas outras respostas = se você estiver usando enumerações C ++ 11 fortemente tipadas, não poderá usá -las ++ou + intnelas. Nesse caso, é necessária uma solução um pouco mais bagunçada:
enumCount{ zero, one, two, three };
for_range (Count, c, zero, three){
cout <<"forward: "<< c << endl;}
Pode ser usado para iterar para frente e para trás através de números inteiros, enumerados e enumerados não assinados:
for_range (unsigned, i,10,0){
cout <<"backwards i: "<< i << endl;}
for_range (char, c,'z','a'){
cout << c << endl;}
Apesar de sua definição incômoda, ele é otimizado muito bem. Eu olhei desmontador em VC ++. O código é extremamente eficiente. Não desanime, mas as três instruções for: o compilador produzirá apenas um loop após a otimização! Você pode até definir loops fechados:
unsigned p[4][5];
for_range (Count, i, zero,three)
for_range(unsignedint, j,4,0){
p[i][j]=static_cast<unsigned>(i)+j;}
Você obviamente não pode iterar por tipos enumerados com lacunas.
Você não pode sobrecarregar nenhum operador nos tipos enumerados C ou C ++. A menos que você crie uma estrutura / classe que emule uma enumeração de valores.
Sobrecarregando o incremento / decréscimo requer tomar uma decisão sobre o que fazer quando há um excesso
epónimo
3
Assumir que o enum é numerado sequencialmente é propenso a erros. Além disso, convém iterar apenas sobre enumeradores selecionados. Se esse subconjunto for pequeno, fazer um loop sobre ele explicitamente pode ser uma escolha elegante:
enumItem{Man,Wolf,Goat,Cabbage};// or enum classfor(auto item :{Wolf,Goat,Cabbage}){// or Item::Wolf, ...// ...}
Esta é uma boa opção, eu acho. Deve fazer parte de uma especificação C ++ mais recente do que eu estava usando quando fiz a pergunta que estou adivinhando?
Adam
Sim. Ele itera sobre um std :: initializer_list <Item>. link .
22719 marski
2
Se você não gosta de poluir sua enumeração com um item COUNT final (porque talvez se você também usar a enumeração em um comutador, o compilador avisará sobre um caso ausente COUNT :), você pode fazer o seguinte:
enumColour{Red,Green,Blue};constColourLastColour=Blue;Colour co(0);while(true){// do stuff with co// ...if(co ==LastColour)break;
co =Colour(co+1);}
Aqui está outra solução que funciona apenas para enumerações contíguas. Ele fornece a iteração esperada, exceto a feiura no incremento, que é o lugar a que pertence, pois é isso que está quebrado no C ++.
enumBar{One=1,Two,Three,End_Bar// Marker for end of enum; };for(Bar foo =One; foo <End_Bar; foo =Bar(foo +1)){// ...}
Incrementar pode ser reduzido para foo = Bar(foo + 1).
precisa saber é o seguinte
Obrigado, HolyBlackCat, incorporei sua excelente sugestão! Percebo também que o Riot tem praticamente essa mesma solução, mas está em conformidade com a digitação forte (e, portanto, mais detalhada).
Ethan Bradford
2
enumclass A {
a0=0, a3=3, a4=4};constexpr std::array<A,3> ALL_A {A::a0, A::a3, A::a4};// constexpr is important herefor(A a: ALL_A){if(a==A::a0 || a==A::a4) std::cout <<static_cast<int>(a);}
A constexpr std::arraypode iterar até enumerações não sequenciais sem que a matriz seja instanciada pelo compilador. Isso depende de coisas como as heurísticas de otimização do compilador e se você usa o endereço da matriz.
Nas minhas experiências, descobri que o g++9.1 com -O3otimizará a matriz acima se houver 2 valores não sequenciais ou alguns valores sequenciais (testei até 6). Mas isso só acontece se você tiver uma ifdeclaração. (Tentei uma instrução que comparava um valor inteiro maior que todos os elementos em uma matriz seqüencial e ela delineava a iteração apesar de nenhuma ser excluída, mas quando deixei de fora a instrução if, os valores foram colocados na memória.) valores de uma enumeração não sequencial em [um caso | https://godbolt.org/z/XuGtoc] . Suspeito que esse comportamento estranho se deva a heurísticas profundas relacionadas a caches e previsão de ramificação.
Eu gosto de semânticas simples de loop for de alcance e acho que evoluirá ainda mais, e é por isso que gosto dessa solução.
rtischer8277 24/03
2
No livro de linguagem de programação C ++ de Bjarne Stroustrup, você pode ler que ele está propondo sobrecarregar o operator++específico enum. enumsão tipos definidos pelo usuário e o operador de sobrecarga existe no idioma para essas situações específicas.
Você poderá codificar o seguinte:
#include<iostream>enumclassColors{red, green, blue};Colors&operator++(Colors&c,int){switch(c){caseColors::red:return c=Colors::green;caseColors::green:return c=Colors::blue;caseColors::blue:return c=Colors::red;// managing overflowdefault:throw std::exception();// or do anything else to manage the error...}}int main(){Colors c =Colors::red;// casting in int just for convenience of output.
std::cout <<(int)c++<< std::endl;
std::cout <<(int)c++<< std::endl;
std::cout <<(int)c++<< std::endl;
std::cout <<(int)c++<< std::endl;
std::cout <<(int)c++<< std::endl;return0;}
Mente que estou usando enum class. Código funciona bem com enumtambém. Mas eu prefiro, enum classpois eles são de tipo forte e podem nos impedir de cometer erros no momento da compilação.
Um voto negativo foi feito nesta postagem. Alguma razão para não responder à pergunta?
LAL
O motivo é provavelmente porque esta é uma solução terrível em termos de arquitetura: força você a escrever uma lógica global destinada a um componente específico (sua enumeração); além disso, se sua enumeração mudar por qualquer motivo, você será forçado a editar seu + + operador também, como uma abordagem que não é sustentável para nenhum projeto de médio e grande porte, não é uma surpresa que venha da recomendação de Bjarne Stroustrup, na época em que a arquitetura de software era como ficção científica
Manjia
A pergunta original é sobre ter operador para um enum. Não era uma questão arquitetônica. Não acredito que em 2013 o C ++ fosse uma ficção científica.
LAL
1
Para compiladores MS:
#define inc_enum(i)((decltype(i))((int)i +1))enum enumtype { one, two, three, count};for(enumtype i = one; i < count; i = inc_enum(i)){
dostuff(i);}
Nota: esse código é muito menos do que a resposta simples do iterador personalizado com modelo.
Você pode fazer isso funcionar com o GCC usando em typeofvez de decltype, mas não tenho esse compilador à mão no momento para garantir que ele seja compilado.
Isso foi escrito ~ 5 anos depois de decltypese tornar C ++ padrão, portanto você não deve recomendar o desatualizado typeofdo antigo GCC. O GCC vagamente recente lida decltypemuito bem. Há outros problemas: as conversões no estilo C são desencorajadas e as macros piores. Recursos adequados do C ++ podem fornecer a mesma funcionalidade genérica. Isso seria melhor reescrito para usar static_caste uma função de modelo: template <typename T> auto inc_enum(T const t) { return static_cast<T>(static cast<int>(t) + 1); }. E elencos não são necessários para não enum class. Como alternativa, os operadores podem ser sobrecarregados por enumtipo (TIL)
underscore_d
1
typedefenum{
first =2,
second =6,
third =17}MyEnum;staticconstint enumItems[]={
first,
second,
third
}staticconstintEnumLength=sizeof(enumItems)/sizeof(int);for(int i =0; i <EnumLength; i++){//Do something with enumItems[i]}
Você poderia me explicar por que a "introspecção" seria necessária para iterar sobre um enum?
Jonathan Mee
Talvez o termo seja Reflexão ?
kͩeͣmͮpͥ # 8/16
2
Estou tentando dizer duas coisas: 1) Por muitas outras respostas, o C ++ pode fazer isso; portanto, se você disser que não pode, é necessário um link ou esclarecimento adicional. 2) Na sua forma atual, este é, na melhor das hipóteses, um comentário, certamente não uma resposta.
Jonathan Mee
Voto negativo a
1
Vou me concentrar novamente em 2 comentários: 1) Não voto negativo porque acho que receber uma participação no site desmotiva desmotiva, acho isso contraproducente 2) Ainda não entendo o que você está tentando dizer, mas parece você entende algo que eu não entendo; nesse caso, eu prefiro que você elabore em vez de excluir uma resposta com voto negativo.
Jonathan Mee
0
Se você soubesse que os valores da enumeração eram seqüenciais, por exemplo, a enumeração Qt: Key, você poderia:
int
pode não ser grande o suficiente! ([C++03: 7.2/5]
)operator++
enums; no entanto, para que você possa fazerfor(Enum_E e = (Enum_E)0; e < ENUM_COUNT; e++)
. Observe que você precisa converter0
paraEnum_E
porque o C ++ proíbe operadores de atribuição em enumerações.Respostas:
A maneira típica é a seguinte:
Observe que o enum
Last
deve ser ignorado pela iteração. Utilizando essaLast
enumeração "falsa" , você não precisa atualizar sua condição final no loop for para a última enumeração "real" toda vez que desejar adicionar uma nova enumeração. Se você quiser adicionar mais enumerações posteriormente, basta adicioná-las antes do Último. O loop neste exemplo ainda funcionará.Obviamente, isso será interrompido se os valores de enumeração forem especificados:
Isso ilustra que um enum não é realmente destinado a iterar. A maneira típica de lidar com uma enumeração é usá-la em uma instrução switch.
Se você realmente deseja enumerar, encha os valores de enum em um vetor e itere sobre ele. Isso também lidará adequadamente com os valores de enum especificados.
fonte
UNKNOWN = 0
. Além disso, eu sugeriria apenas descartar odefault
caso ao alternar valores de enum, pois ele pode ocultar casos em que o tratamento de valores foi esquecido até o tempo de execução. Em vez disso, deve-se codificar todos os valores e usar oUNKNOWN
campo para detectar incompatibilidades.fonte
static const Type All[3];
e então eu posso para inicializar na fonte:const MyEnum::Type MyEnum::All[3] = { a, b, c };
Antes de fazer isso, eu estava recebendoError in range-based for...
erros desagradáveis (porque a matriz tinha um tamanho desconhecido). Descobriram isso graças a uma resposta relacionadaSe o seu enum começa com 0 e o incremento é sempre 1.
Se não, acho que o único motivo é criar algo como um
adicione os itens e use iteradores normais ....
fonte
Com o c ++ 11, na verdade existe uma alternativa: escrever um iterador personalizado simples e com modelo.
vamos assumir que seu enum é
Esse código genérico fará o truque, com bastante eficiência - coloque em um cabeçalho genérico, servirá para qualquer enumeração que você precise repetir:
Você precisará se especializar
E então você pode iterar usando o intervalo para
A suposição de que você não tem lacunas na enumeração ainda é verdadeira; não há suposição sobre o número de bits realmente necessário para armazenar o valor da enumeração (graças a std :: subjac_type)
fonte
std::vector
não é genérico porquestd::vector<foo>
está vinculadofoo
.typedef Iterator<color, color::green, color::red> colorIterator;
Certifique-se de entender como as instanciações de modelo funcionam.foo operator*() { ...
deveria serC operator*() { ...
.muito complicado essas soluções, eu faço assim:
fonte
for (NodePosition pos : NodePositionVector)
sintaxe? Tanto quanto sei, essa é a sintaxe Java, e você precisará de iteradores em C ++ para fazer algo equivalente.auto
, assim você não precisa declarar explicitamente o tipo dos elementos, a menos que você (A) precise usar um operador de conversão ou (B) não gosteauto
por algum motivo . A maioria gama-for
usuários usamauto
AFAICTCostumo fazer assim
ou se não for sucessivo, mas com etapa regular (por exemplo, sinalizadores de bit)
fonte
Você não pode com um enum. Talvez um enum não seja o mais adequado para sua situação.
Uma convenção comum é nomear o último valor da enumeração como MAX e usá-lo para controlar um loop usando um int.
fonte
Algo que não foi abordado nas outras respostas = se você estiver usando enumerações C ++ 11 fortemente tipadas, não poderá usá -las
++
ou+ int
nelas. Nesse caso, é necessária uma solução um pouco mais bagunçada:fonte
Você pode tentar definir a seguinte macro:
Agora você pode usá-lo:
Pode ser usado para iterar para frente e para trás através de números inteiros, enumerados e enumerados não assinados:
Apesar de sua definição incômoda, ele é otimizado muito bem. Eu olhei desmontador em VC ++. O código é extremamente eficiente. Não desanime, mas as três instruções for: o compilador produzirá apenas um loop após a otimização! Você pode até definir loops fechados:
Você obviamente não pode iterar por tipos enumerados com lacunas.
fonte
_A1
não é um nome permitido, é um sublinhado à esquerda com uma letra maiúscula a seguir.Você também pode sobrecarregar os operadores de incremento / decremento para seu tipo enumerado.
fonte
Assumir que o enum é numerado sequencialmente é propenso a erros. Além disso, convém iterar apenas sobre enumeradores selecionados. Se esse subconjunto for pequeno, fazer um loop sobre ele explicitamente pode ser uma escolha elegante:
fonte
Se você não gosta de poluir sua enumeração com um item COUNT final (porque talvez se você também usar a enumeração em um comutador, o compilador avisará sobre um caso ausente COUNT :), você pode fazer o seguinte:
fonte
Aqui está outra solução que funciona apenas para enumerações contíguas. Ele fornece a iteração esperada, exceto a feiura no incremento, que é o lugar a que pertence, pois é isso que está quebrado no C ++.
fonte
foo = Bar(foo + 1)
.A
constexpr std::array
pode iterar até enumerações não sequenciais sem que a matriz seja instanciada pelo compilador. Isso depende de coisas como as heurísticas de otimização do compilador e se você usa o endereço da matriz.Nas minhas experiências, descobri que o
g++
9.1 com-O3
otimizará a matriz acima se houver 2 valores não sequenciais ou alguns valores sequenciais (testei até 6). Mas isso só acontece se você tiver umaif
declaração. (Tentei uma instrução que comparava um valor inteiro maior que todos os elementos em uma matriz seqüencial e ela delineava a iteração apesar de nenhuma ser excluída, mas quando deixei de fora a instrução if, os valores foram colocados na memória.) valores de uma enumeração não sequencial em [um caso | https://godbolt.org/z/XuGtoc] . Suspeito que esse comportamento estranho se deva a heurísticas profundas relacionadas a caches e previsão de ramificação.Aqui está um link para uma iteração de teste simples no godbolt que demonstra que o array nem sempre é instanciado.
O preço dessa técnica é escrever os elementos da enumeração duas vezes e manter as duas listas sincronizadas.
fonte
No livro de linguagem de programação C ++ de Bjarne Stroustrup, você pode ler que ele está propondo sobrecarregar o
operator++
específicoenum
.enum
são tipos definidos pelo usuário e o operador de sobrecarga existe no idioma para essas situações específicas.Você poderá codificar o seguinte:
código de teste: http://cpp.sh/357gb
Mente que estou usando
enum class
. Código funciona bem comenum
também. Mas eu prefiro,enum class
pois eles são de tipo forte e podem nos impedir de cometer erros no momento da compilação.fonte
enum
. Não era uma questão arquitetônica. Não acredito que em 2013 o C ++ fosse uma ficção científica.Para compiladores MS:
Nota: esse código é muito menos do que a resposta simples do iterador personalizado com modelo.
Você pode fazer isso funcionar com o GCC usando em
typeof
vez dedecltype
, mas não tenho esse compilador à mão no momento para garantir que ele seja compilado.fonte
decltype
se tornar C ++ padrão, portanto você não deve recomendar o desatualizadotypeof
do antigo GCC. O GCC vagamente recente lidadecltype
muito bem. Há outros problemas: as conversões no estilo C são desencorajadas e as macros piores. Recursos adequados do C ++ podem fornecer a mesma funcionalidade genérica. Isso seria melhor reescrito para usarstatic_cast
e uma função de modelo:template <typename T> auto inc_enum(T const t) { return static_cast<T>(static cast<int>(t) + 1); }
. E elencos não são necessários para nãoenum class
. Como alternativa, os operadores podem ser sobrecarregados porenum
tipo (TIL)fonte
constexpr static const int enumItems[]
Como o C ++ não possui introspecção, não é possível determinar esse tipo de coisa em tempo de execução.
fonte
Se você soubesse que os valores da enumeração eram seqüenciais, por exemplo, a enumeração Qt: Key, você poderia:
Funciona como esperado.
fonte
Apenas faça uma matriz de entradas e faça um loop sobre a matriz, mas faça o último elemento dizer -1 e use-o para a condição de saída.
Se enum for:
então crie uma matriz:
Isso não se decompõe, independentemente das entradas na representação, desde que a verificação -1 não colide com um dos elementos, é claro.
fonte