Quando alguém afirma que alguma construção de programação é "má", está tentando desencorajá-lo a pensar por si mesmo.
Pete Becker
3
@ NicolBolas: Essa é mais uma pergunta retórica para fornecer uma resposta para perguntas frequentes (se essa é realmente a pergunta Frequenty é outra história).
David Rodríguez - dribeas
@ David, há uma discussão sobre se isso deve ser um FAQ ou não, que começa aqui . Entrada bem-vinda.
SBI
17
@PeteBecker Às vezes, eles estão apenas tentando protegê- lo de si mesmo.
enumclassColor{ red, green, blue };// enum classenumAnimal{ dog, cat, bird, human };// plain enum
Qual é a diferença entre two?
enum classes - nomes de enumeradores são locais para a enumeração e seus valores não se convertem implicitamente em outros tipos (como outro enumou int)
Plain enums - onde os nomes dos enumeradores estão no mesmo escopo que a enum e seus valores implicitamente se convertem em números inteiros e outros tipos
Exemplo:
enumColor{ red, green, blue };// plain enum enumCard{ red_card, green_card, yellow_card };// another plain enum enumclassAnimal{ dog, deer, cat, bird, human };// enum classenumclassMammal{ kangaroo, deer, human };// another enum classvoid fun(){// examples of bad use of plain enums:Color color =Color::red;Card card =Card::green_card;int num = color;// no problemif(color ==Card::red_card)// no problem (bad)
cout <<"bad"<< endl;if(card ==Color::green)// no problem (bad)
cout <<"bad"<< endl;// examples of good use of enum classes (safe)Animal a =Animal::deer;Mammal m =Mammal::deer;int num2 = a;// errorif(m == a)// error (good)
cout <<"bad"<< endl;if(a ==Mammal::deer)// error (good)
cout <<"bad"<< endl;}
Conclusão:
enum classes deve ser preferido, pois causa menos surpresas que podem levar a erros.
Bom exemplo ... existe uma maneira de combinar a segurança de tipo da versão da classe com a promoção de namespace da versão enum? Ou seja, se eu tiver uma classe Acom state e criar um enum class State { online, offline };como filho da classe A, gostaria de fazer state == onlineverificações dentro de em Avez de state == State::online... isso é possível?
marca de
31
Não. A promoção do namespace é uma coisa ruim e metade da justificativa enum classera eliminá-la.
Filhote de cachorro
10
Em C ++ 11, você pode usar enums explicitamente digitados também, como animal enum: int não assinado {cão, veados, gato, pássaro}
Blasius Secundus
3
@ Cat Plus Plus Entendo que @Oleksiy diz que é ruim. Minha pergunta não era se Oleksiy pensava que era ruim. Minha pergunta foi um pedido para detalhar o que há de ruim nisso. Especificamente, por que Oleksiy, por exemplo, considera ruim Color color = Color::red.
chux - Reinstala Monica
9
@ Cat Plus Plus Portanto, o exemplo ruim não ocorre até a if (color == Card::red_card)linha, quatro linhas depois do comentário (o que eu vejo agora se aplica à primeira metade do bloco.) 2 linhas do bloco dão exemplos ruins . As 3 primeiras linhas não são um problema. O "bloco inteiro é o motivo pelo qual as enums comuns são ruins" me jogou como eu pensei que você quisesse dizer que havia algo errado com elas também. Entendo agora, é apenas uma configuração. De qualquer forma, obrigado pelo feedback.
As enum classes ("novas enumerações", "enumerações fortes") abordam três problemas com as enumerações C ++ tradicionais:
enumerações convencionais convertem implicitamente em int, causando erros quando alguém não deseja que uma enumeração atue como um número inteiro.
enums convencionais exportam seus enumeradores para o escopo circundante, causando conflitos de nome.
o tipo subjacente de um enumnão pode ser especificado, causando confusão, problemas de compatibilidade e impossibilitando a declaração avançada.
As novas enumerações são "classe de enumeração" porque combinam aspectos de enumerações tradicionais (valores de nomes) com aspectos de classes (membros com escopo definido e ausência de conversões).
Portanto, como mencionado por outros usuários, as "enumerações fortes" tornariam o código mais seguro.
O tipo subjacente de um "clássico" enumdeve ser um tipo inteiro grande o suficiente para caber em todos os valores do enum; isso geralmente é um int. Além disso, cada tipo enumerado deve ser compatível com charou um tipo inteiro assinado / não assinado.
Esta é uma descrição ampla do que um enumtipo subjacente deve ser; portanto, cada compilador tomará suas próprias decisões sobre o tipo subjacente do clássico enume, às vezes, o resultado pode ser surpreendente.
Por exemplo, eu já vi código assim várias vezes:
enum E_MY_FAVOURITE_FRUITS
{
E_APPLE =0x01,
E_WATERMELON =0x02,
E_COCONUT =0x04,
E_STRAWBERRY =0x08,
E_CHERRY =0x10,
E_PINEAPPLE =0x20,
E_BANANA =0x40,
E_MANGO =0x80,
E_MY_FAVOURITE_FRUITS_FORCE8 =0xFF// 'Force' 8bits, how can you tell?};
No código acima, algum codificador ingênuo está pensando que o compilador armazenará os E_MY_FAVOURITE_FRUITSvalores em um tipo de 8 bits não assinado ... mas não há garantia: o compilador pode escolher unsigned charou intou short, qualquer um desses tipos é grande o suficiente para caber em todos os valores vistos no enum. Adicionar um campo E_MY_FAVOURITE_FRUITS_FORCE8é um fardo e não força o compilador a fazer qualquer tipo de escolha sobre o tipo subjacente do enum.
Se houver algum pedaço de código que dependa do tamanho do tipo e / ou suponha que E_MY_FAVOURITE_FRUITStenha alguma largura (por exemplo: rotinas de serialização), esse código poderá se comportar de maneiras estranhas, dependendo dos pensamentos do compilador.
E para piorar a situação, se algum colega de trabalho acrescentar de forma descuidada um novo valor ao nosso enum:
E_DEVIL_FRUIT =0x100,// New fruit, with value greater than 8bits
O compilador não se queixa! Ele apenas redimensiona o tipo para caber em todos os valores de enum(assumindo que o compilador esteja usando o menor tipo possível, o que é uma suposição que não podemos fazer). Esta adição simples e descuidada aoenum sutileza poderia quebrar códigos relacionados.
Como o C ++ 11 é possível especificar o tipo subjacente para enume enum class(obrigado rdb ), esse problema foi resolvido de maneira ordenada:
Especificando o tipo subjacente, se um campo tiver uma expressão fora do intervalo desse tipo, o compilador reclamará em vez de alterar o tipo subjacente.
Eu acho que isso é uma boa melhoria de segurança.
Então, por que a classe enum é preferida à enum comum? , se podemos escolher o tipo subjacente para enumerações com escopo ( enum class) e sem escopo ( enum), o que mais faz enum classuma escolha melhor ?:
Suponho que pode restringir o tipo de base enum dentro para enums regulares, bem como, desde que temos C ++ 11
Sagar Padhye
11
Desculpe, mas esta resposta está errada. "classe enum" não tem nada a ver com a capacidade de especificar o tipo. Esse é um recurso independente que existe tanto para enumerações regulares quanto para enumerações.
Rdb 06/06
14
Este é o problema: * As classes Enum são um novo recurso do C ++ 11. * As enumerações digitadas são um novo recurso do C ++ 11. Esses são dois novos recursos independentes não relacionados no C ++ 11. Você pode usar ambos, ou você pode usar um ou nenhum.
Rdb 6/05
2
Eu acho que Alex Allain fornece a explicação simples mais completa que eu já vi neste blog em [ cprogramming.com/c++11/… . A enum tradicional era boa para usar nomes em vez de valores inteiros e evitar o pré-processador #defines, que era uma coisa boa - acrescentava clareza. A classe enum remove o conceito de um valor numérico do enumerador e introduz o escopo e a digitação forte que aumentam (bem, podem aumentar :-) a correção do programa. Ele o aproxima um passo do pensamento orientado a objetos.
Jon Spencer
2
Como um aparte, é sempre divertido quando você está revisando o código e de repente One Piece acontece.
Justin Time - Restabelece Monica
47
A vantagem básica de usar a classe enum sobre enumerações normais é que você pode ter as mesmas variáveis de enumeração para duas enumerações diferentes e ainda pode resolvê-las (que foi mencionada como tipo segura pelo OP)
Por exemplo:
enumclassColor1{ red, green, blue };//this will compileenumclassColor2{ red, green, blue };enumColor1{ red, green, blue };//this will not compile enumColor2{ red, green, blue };
Quanto às enumerações básicas, o compilador não será capaz de distinguir se redestá se referindo ao tipo Color1ou Color2como na declaração abaixo.
enumColor1{ red, green, blue };enumColor2{ red, green, blue };int x = red;//Compile time error(which red are you refering to??)
@Oleksiy Ohh, eu não li sua pergunta corretamente. Considerar é um complemento para quem não sabia.
Saksham
está certo! Eu quase me esqueci sobre isso
Oleksiy
é claro, você escreveria enum { COLOR1_RED, COLOR1_GREE, COLOR1_BLUE }, evitando facilmente problemas de espaço para nome. O argumento do espaço para nome é um dos três mencionados aqui que eu não compro.
Jo #
2
@ Jo Então essa solução é uma solução desnecessária. Ajustes: enum Color1 { COLOR1_RED, COLOR1_GREEN, COLOR1_BLUE }é comparável a classe Enum: enum class Color1 { RED, GREEN, BLUE }. O acesso é semelhante: COLOR1_REDvs Color1::RED, mas a versão Enum exige que você digite "COLOR1" em cada valor, o que oferece mais espaço para erros de digitação, o que evita o comportamento do espaço de nome de uma classe enum.
cdgraham
2
Por favor, use críticas construtivas . Quando digo mais espaço para erros de digitação, quero dizer quando você define originalmente os valores de enum Color1, dos quais um compilador não pode capturar, pois provavelmente ainda seria um nome 'válido'. Se eu escrever RED, GREENe assim por diante, usando uma classe enum, isso não pode ser resolvido enum Bananaporque requer que você especifique Color1::REDpara acessar o valor (o argumento do espaço para nome). Ainda há bons momentos para usar enum, mas o comportamento do espaço para nome de um enum classpode frequentemente ser muito benéfico.
cdgraham
20
Enumerações são usadas para representar um conjunto de valores inteiros.
A classpalavra-chave após enumespecifica que a enumeração é fortemente digitada e seus enumeradores têm escopo definido. Dessa forma, as enumclasses evitam o uso acidental de constantes.
enumerações convencionais convertem implicitamente em int, causando erros quando alguém não deseja que uma enumeração atue como um número inteiro.
enum color
{Red,Green,Yellow};enumclassNewColor{Red_1,Green_1,Yellow_1};int main(){//! Implicit conversion is possibleint i =Red;//! Need enum class name followed by access specifier. Ex: NewColor::Red_1int j =Red_1;// error C2065: 'Red_1': undeclared identifier//! Implicit converison is not possible. Solution Ex: int k = (int)NewColor::Red_1;int k =NewColor::Red_1;// error C2440: 'initializing': cannot convert from 'NewColor' to 'int'return0;}
enums convencionais exportam seus enumeradores para o escopo circundante, causando conflitos de nome.
// Header.henum vehicle
{Car,Bus,Bike,Autorickshow};enumFourWheeler{Car,// error C2365: 'Car': redefinition; previous definition was 'enumerator'SmallBus};enumclassEditor{
vim,
eclipes,VisualStudio};enumclassCppEditor{
eclipes,// No error of redefinitionsVisualStudio,// No error of redefinitionsQtCreator};
O tipo subjacente de uma enumeração não pode ser especificado, causando confusão, problemas de compatibilidade e impossibilitando a declaração avançada.
O C ++ 11 também permite digitar enumerações "sem classe" . Os problemas de poluição do espaço de nome, etc., ainda existem. Dê uma olhada nas respostas relevantes que existiam muito tempo antes desta ..
user2864740 17/02
7
não converte implicitamente em int
pode escolher qual o tipo subjacente
Espaço de nome ENUM para evitar a poluição
Comparado com a classe normal, pode ser declarado adiante, mas não possui métodos
Vale ressaltar, além dessas outras respostas, que o C ++ 20 resolve um dos problemas que enum classtem: verbosidade. Imaginando um hipotético enum class, Color.
Para concluir meu comentário anterior, observe que o gcc agora possui um aviso chamado -Wint-in-bool-context que capturará exatamente esse tipo de erro.
Arnaud
0
Uma coisa que não foi mencionada explicitamente - o recurso de escopo oferece a opção de ter o mesmo nome para um método de enumeração e classe. Por exemplo:
classTest{public:// these call ProcessCommand() internallyvoidTakeSnapshot();voidRestoreSnapshot();private:enumclassCommand// wouldn't be possible without 'class'{TakeSnapshot,RestoreSnapshot};voidProcessCommand(Command cmd);// signal the other thread or whatever};
enum
vsenum class
.Respostas:
C ++ possui dois tipos de
enum
:enum class
esenum
sAqui estão alguns exemplos de como declará-los:
Qual é a diferença entre two?
enum class
es - nomes de enumeradores são locais para a enumeração e seus valores não se convertem implicitamente em outros tipos (como outroenum
ouint
)Plain
enum
s - onde os nomes dos enumeradores estão no mesmo escopo que a enum e seus valores implicitamente se convertem em números inteiros e outros tiposExemplo:
Conclusão:
enum class
es deve ser preferido, pois causa menos surpresas que podem levar a erros.fonte
A
com state e criar umenum class State { online, offline };
como filho da classeA
, gostaria de fazerstate == online
verificações dentro de emA
vez destate == State::online
... isso é possível?enum class
era eliminá-la.Color color = Color::red
.if (color == Card::red_card)
linha, quatro linhas depois do comentário (o que eu vejo agora se aplica à primeira metade do bloco.) 2 linhas do bloco dão exemplos ruins . As 3 primeiras linhas não são um problema. O "bloco inteiro é o motivo pelo qual as enums comuns são ruins" me jogou como eu pensei que você quisesse dizer que havia algo errado com elas também. Entendo agora, é apenas uma configuração. De qualquer forma, obrigado pelo feedback.Do FAQ C ++ 11 de Bjarne Stroustrup :
Portanto, como mencionado por outros usuários, as "enumerações fortes" tornariam o código mais seguro.
O tipo subjacente de um "clássico"
enum
deve ser um tipo inteiro grande o suficiente para caber em todos os valores doenum
; isso geralmente é umint
. Além disso, cada tipo enumerado deve ser compatível comchar
ou um tipo inteiro assinado / não assinado.Esta é uma descrição ampla do que um
enum
tipo subjacente deve ser; portanto, cada compilador tomará suas próprias decisões sobre o tipo subjacente do clássicoenum
e, às vezes, o resultado pode ser surpreendente.Por exemplo, eu já vi código assim várias vezes:
No código acima, algum codificador ingênuo está pensando que o compilador armazenará os
E_MY_FAVOURITE_FRUITS
valores em um tipo de 8 bits não assinado ... mas não há garantia: o compilador pode escolherunsigned char
ouint
oushort
, qualquer um desses tipos é grande o suficiente para caber em todos os valores vistos noenum
. Adicionar um campoE_MY_FAVOURITE_FRUITS_FORCE8
é um fardo e não força o compilador a fazer qualquer tipo de escolha sobre o tipo subjacente doenum
.Se houver algum pedaço de código que dependa do tamanho do tipo e / ou suponha que
E_MY_FAVOURITE_FRUITS
tenha alguma largura (por exemplo: rotinas de serialização), esse código poderá se comportar de maneiras estranhas, dependendo dos pensamentos do compilador.E para piorar a situação, se algum colega de trabalho acrescentar de forma descuidada um novo valor ao nosso
enum
:O compilador não se queixa! Ele apenas redimensiona o tipo para caber em todos os valores de
enum
(assumindo que o compilador esteja usando o menor tipo possível, o que é uma suposição que não podemos fazer). Esta adição simples e descuidada aoenum
sutileza poderia quebrar códigos relacionados.Como o C ++ 11 é possível especificar o tipo subjacente para
enum
eenum class
(obrigado rdb ), esse problema foi resolvido de maneira ordenada:Especificando o tipo subjacente, se um campo tiver uma expressão fora do intervalo desse tipo, o compilador reclamará em vez de alterar o tipo subjacente.
Eu acho que isso é uma boa melhoria de segurança.
Então, por que a classe enum é preferida à enum comum? , se podemos escolher o tipo subjacente para enumerações com escopo (
enum class
) e sem escopo (enum
), o que mais fazenum class
uma escolha melhor ?:int
.fonte
A vantagem básica de usar a classe enum sobre enumerações normais é que você pode ter as mesmas variáveis de enumeração para duas enumerações diferentes e ainda pode resolvê-las (que foi mencionada como tipo segura pelo OP)
Por exemplo:
Quanto às enumerações básicas, o compilador não será capaz de distinguir se
red
está se referindo ao tipoColor1
ouColor2
como na declaração abaixo.fonte
enum { COLOR1_RED, COLOR1_GREE, COLOR1_BLUE }
, evitando facilmente problemas de espaço para nome. O argumento do espaço para nome é um dos três mencionados aqui que eu não compro.enum Color1 { COLOR1_RED, COLOR1_GREEN, COLOR1_BLUE }
é comparável a classe Enum:enum class Color1 { RED, GREEN, BLUE }
. O acesso é semelhante:COLOR1_RED
vsColor1::RED
, mas a versão Enum exige que você digite "COLOR1" em cada valor, o que oferece mais espaço para erros de digitação, o que evita o comportamento do espaço de nome de uma classe enum.enum Color1
, dos quais um compilador não pode capturar, pois provavelmente ainda seria um nome 'válido'. Se eu escreverRED
,GREEN
e assim por diante, usando uma classe enum, isso não pode ser resolvidoenum Banana
porque requer que você especifiqueColor1::RED
para acessar o valor (o argumento do espaço para nome). Ainda há bons momentos para usarenum
, mas o comportamento do espaço para nome de umenum class
pode frequentemente ser muito benéfico.Enumerações são usadas para representar um conjunto de valores inteiros.
A
class
palavra-chave apósenum
especifica que a enumeração é fortemente digitada e seus enumeradores têm escopo definido. Dessa forma, asenum
classes evitam o uso acidental de constantes.Por exemplo:
Aqui não podemos misturar valores de animais e animais de estimação.
fonte
As perguntas frequentes do C ++ 11 mencionam os pontos abaixo:
enumerações convencionais convertem implicitamente em int, causando erros quando alguém não deseja que uma enumeração atue como um número inteiro.
enums convencionais exportam seus enumeradores para o escopo circundante, causando conflitos de nome.
O tipo subjacente de uma enumeração não pode ser especificado, causando confusão, problemas de compatibilidade e impossibilitando a declaração avançada.
.
.
fonte
fonte
Vale ressaltar, além dessas outras respostas, que o C ++ 20 resolve um dos problemas que
enum class
tem: verbosidade. Imaginando um hipotéticoenum class
,Color
.Isso é detalhado comparado à
enum
variação simples , onde os nomes estão no escopo global e, portanto, não precisam ser prefixadosColor::
.No entanto, no C ++ 20, podemos usar
using enum
para introduzir todos os nomes em uma enumeração no escopo atual, resolvendo o problema.Portanto, agora, não há razão para não usar
enum class
.fonte
Como, como dito em outras respostas, a classe enum não é implicitamente convertível em int / bool, também ajuda a evitar códigos de bugs, como:
fonte
Uma coisa que não foi mencionada explicitamente - o recurso de escopo oferece a opção de ter o mesmo nome para um método de enumeração e classe. Por exemplo:
fonte