Como usar enumerações em C ++

218

Suponha que tenhamos enumo seguinte:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};

Eu quero criar uma instância disso enume inicializá-lo com um valor adequado, então eu faço:

Days day = Days.Saturday;

Agora eu quero verificar minha variável ou instância com um enumvalor existente , então eu faço:

if (day == Days.Saturday)
{
    std::cout << "Ok its Saturday";
}

O que me dá um erro de compilação:

erro: expressão primária esperada antes de '.' símbolo

Então, para ficar claro, qual é a diferença entre dizer:

if (day == Days.Saturday) // Causes compilation error

e

if (day == Saturday)

?

A que esses dois realmente se referem, em que um está OK e um causa um erro de compilação?

Rika
fonte
4
Eu sei, eu quero saber por que está me dando o erro!
Rika
1
É quarta-feira aqui. Você tem muitos erros de sintaxe para o compilador C ++. A partir de 'Enum'.
Öö Tiib
1
@Hossein, porque enums não são a mesma sintaxe (e semântica) nos dois idiomas. A primeira coisa que faço depois de receber um erro ao tentar usar um recurso em um novo idioma é procurar a sintaxe (ou se for possível) nesse idioma.
28412 chris
@ Chris: Eu sei, eu faço exatamente o mesmo thing.hopefully eu tenho o meu answer.I também atualizou a questão a ser mais clearer.Thank você pela maneira;)
Rika
17
" até onde eu sei, a declaração e o uso das enumerações nesses dois idiomas são iguais. " Aqui está o seu problema. C # não é o mesmo idioma que C ++. Particularmente, eles têm sintaxe diferente para enumerações.
Robᵩ

Respostas:

350

Este código está errado:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Days.Saturday;
if (day == Days.Saturday)

Porque Daysnão é um escopo, nem um objeto. É um tipo. E os próprios tipos não têm membros. O que você escreveu é o equivalente a std::string.clear. std::stringé um tipo, então você não pode usá .-lo. Você usa .em uma instância de uma classe.

Infelizmente, as enums são mágicas e, portanto, a analogia pára por aí. Porque com uma classe, você pode fazer std::string::clearpara obter um ponteiro para a função de membro, mas no C ++ 03, Days::Sundayé inválido. (O que é triste). Isso ocorre porque o C ++ é (um pouco) retrocompatível com C e C não tinha namespaces, portanto, as enumerações precisavam estar no namespace global. Portanto, a sintaxe é simplesmente:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday)

Felizmente, Mike Seymour observa que isso foi abordado no C ++ 11. Mude enumpara enum classe obtém seu próprio escopo; portanto, Days::Sundaynão é apenas válido, mas é a única maneira de acessar Sunday. Dias felizes!

Mooing Duck
fonte
254
Felizmente, sua reclamação foi resolvida em C ++ 11. Mude enumpara enum classe obtém seu próprio escopo; portanto, Days::Sundaynão é apenas válido, mas é a única maneira de acessar Sunday. Dias felizes!
Mike Seymour
11
É preciso amar as mensagens de erro do C ++ ... elas provam que a linguagem é muito complicada para até dar um bom feedback. Considero que uma 'expressão primária' é um objeto ou um escopo ou alguma outra coisa que NÃO é um tipo. Talvez um tipo seja uma 'expressão secundária'. E o que um desenvolvedor de C ++ pode chamar de 'operador de ponto', o compilador de C ++ pode chamar apenas de 'token'. Quando fica difícil entender as mensagens de erro, há algo errado com o idioma que eu acho.
Travis
4
@ Travis: en.cppreference.com/w/cpp/language/… . Uma expressão primária é apenas a primeira coisa em uma expressão, geralmente um nome ou variável ou literal. Quanto à segunda parte, não vejo grande diferença entre '.' tokene dot operator, exceto um símbolo e não um operador, e mostra o símbolo exato, e não um nome.
Mooing Duck
@ Mike Seymour Tentei acessar as enumerações sem os operadores de resolução de escopo em vários compiladores, e parece funcionar. Você disse que no C ++ 11 é a única maneira, por algum motivo eu posso acessar os valores de enumeração como globais, não precisa do
:::
1
@TitoneMaurice: se você tiver um enum, não poderá usar nenhum escopo ou o escopo global ( ::Saturday). Se você tem um enum class(o que é uma coisa muito diferente), é necessário usá-lo Days::Saturday.
Mooing Duck
24

Isso será suficiente para declarar sua variável enum e compará-la:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday) {
    std::cout << "Ok its Saturday";
}
mathematician1975
fonte
por que está errado dizer se (day == Days.Satudday)? eles devem ser iguais, então por que o compilador está reclamando disso?
Rika
1
@Hossein os valores declarados em sua enumeração não se comportam como variáveis ​​de classe ou estrutura de membros. Esta não é a sintaxe correta para usar
mathematician1975
2
@ Hossein: porque Daysnão é um escopo, nem objeto. É um tipo. E os próprios tipos não têm membros. std::string.cleartambém falha ao compilar pelo mesmo motivo.
Mooing Duck
8
@Hossein: Porque não é assim que as enums em C ++ funcionam. Enumerações sem escopo colocam seus valores no espaço para nome circundante; os de escopo ( enum classnovo em 2011) têm escopo próprio e são acessados ​​usando o operador de escopo Days::Saturday,. O operador de acesso a membros ( .) é usado apenas para acessar membros da classe.
Mike Seymour
@MooingDUck e MikeSeymour Um de vocês postaria sua resposta como resposta? porque é exatamente isso que eu procurava depois de emitir esta pergunta;) #
319 Rika
22

Muito disso deve fornecer erros de compilação.

// note the lower case enum keyword
enum Days { Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday };

Agora, Saturday, Sunday, etc. pode ser usado como de nível superior constantes descalços, e Dayspode ser usado como um tipo:

Days day = Saturday;   // Days.Saturday is an error

E da mesma forma mais tarde, para testar:

if (day == Saturday)
    // ...

Esses enumvalores são como constantes nuas - sem escopo - com uma pequena ajuda extra do compilador: (a menos que você esteja usando classes enum C ++ 11 ), eles não são encapsulados como membros de objeto ou estrutura, por exemplo, e você não pode se referir a eles como membros de Days.

Você terá o que procura com o C ++ 11 , que apresenta enum class:

enum class Days
{
    SUNDAY,
    MONDAY,
    // ... etc.
}

// ...

if (day == Days::SUNDAY)
    // ...

Observe que esse C ++ é um pouco diferente de C de duas maneiras: uma é que C requer o uso da enumpalavra - chave ao declarar uma variável:

// day declaration in C:
enum Days day = Saturday;
pb2q
fonte
Eu atualizei a pergunta, eu acho que é agora mais clara que eu sou exatamente após :) A propósito thankyou :)
Rika
14

Você pode usar um truque para usar escopos como desejar, basta declarar enum da seguinte maneira:

struct Days 
{
   enum type
   {
      Saturday,Sunday,Tuesday,Wednesday,Thursday,Friday
   };
};

Days::type day = Days::Saturday;
if (day == Days::Saturday)
ataman1x
fonte
9

Em vez de usar um monte de instruções if, as enums se prestam bem para alternar instruções

Eu uso algumas combinações de enum / switch no construtor de níveis que estou construindo para o meu jogo.

EDIT: Outra coisa, eu vejo que você quer sintaxe semelhante a;

if(day == Days.Saturday)
etc

Você pode fazer isso em C ++:

if(day == Days::Saturday)
etc

Aqui está um exemplo muito simples:

EnumAppState.h

#ifndef ENUMAPPSTATE_H
#define ENUMAPPSTATE_H
enum eAppState
{
    STARTUP,
    EDIT,
    ZONECREATION,
    SHUTDOWN,
    NOCHANGE
};
#endif

Somefile.cpp

#include "EnumAppState.h"
eAppState state = eAppState::STARTUP;
switch(state)
{
case STARTUP:
    //Do stuff
    break;
case EDIT:
    //Do stuff
    break;
case ZONECREATION:
    //Do stuff
    break;
case SHUTDOWN:
    //Do stuff
    break;
case NOCHANGE:
    //Do stuff
    break;
}
Dean Knight
fonte
O bom aqui é que os compiladores irá dizer-lhe se você perdeu colocando um caso no.
chris
Você não deve usar enum de classe neste caso?
Rika
1
enum é apenas um tipo de dados em C ++. Portanto, declarar um enum como eu fiz acima em um arquivo .h e incluir esse arquivo em qualquer arquivo .cpp em que você deseja usá-lo dará acesso ao enum. Só notei que esqueci de adicionar o #include no meu exemplo .cpp. Edição.
Dean Cavaleiro
Além disso, vejo outra pessoa dizendo que enums em C ++ são globais. Na minha experiência, usando enumerações da maneira que descrevi acima, só posso acessá-las quando incluí o .h. Portanto, isso parece impedir o acesso global também, o que é sempre bom. EDIT: Parece que eu estou saber usar enums em C 11 maneira ++ se eu estou lendo as coisas direito ...
Dean Cavaleiro
9

Se você ainda estiver usando o C ++ 03 e quiser usar enumerações, use enumerações dentro de um espaço para nome. Por exemplo:

namespace Daysofweek{
enum Days {Saturday, Sunday, Tuesday,Wednesday, Thursday, Friday};
}

Você pode usar a enumeração fora do namespace, como

Daysofweek::Days day = Daysofweek::Saturday;

if (day == Daysofweek::Saturday)
{
    std::cout<<"Ok its Saturday";
}
San
fonte
8

Você está procurando enumerações fortemente tipadas , um recurso disponível no padrão C ++ 11 . Transforma enumerações em classes com valores de escopo.

Usando seu próprio exemplo de código, é:

  enum class Days {Saturday, Sunday, Tuesday,Wednesday, Thursday, Friday};
  Days day = Days::Saturday;

  if (day == Days::Saturday)  {
    cout << " Today is Saturday !" << endl;
  }
  //int day2 = Days::Sunday; // Error! invalid

O uso ::como acessador de enumerações falhará se tiver como alvo um padrão C ++ anterior ao C ++ 11. Mas alguns compiladores antigos não o suportam, e alguns IDEs simplesmente substituem essa opção e configuram um padrão C ++ antigo.

Se você estiver usando o GCC, ative o C + 11 com -std = c ++ 11 ou -std = gnu11 .

Seja feliz!

Alex Byrth
fonte
1
Você esqueceu de escrever enum class Days { ....
Martin Hennings
De fato. consertando-o! Obrigado.
Alex Byrth # 03
7

Isso não deve funcionar em C ++:

Days.Saturday

Dias não é um escopo ou objeto que contém membros que você pode acessar com o operador de ponto. Essa sintaxe é apenas um ismo de C # e não é legal em C ++.

A Microsoft mantém há muito tempo uma extensão C ++ que permite acessar os identificadores usando o operador de escopo:

enum E { A, B, C };

A;
E::B; // works with Microsoft's extension

Mas isso não é padrão antes do C ++ 11. No C ++ 03, os identificadores declarados em uma enumeração existem apenas no mesmo escopo que o próprio tipo de enumeração.

A;
E::B; // error in C++03

O C ++ 11 torna legal qualificar identificadores de enum com o nome de enum e também introduz classes de enum, que criam um novo escopo para os identificadores em vez de colocá-los no escopo circundante.

A;
E::B; // legal in C++11

enum class F { A, B, C };

A; // error
F::B;
bames53
fonte
4

Infelizmente, os elementos do enum são 'globais'. Você os acessa fazendo day = Saturday. Isso significa que você não pode ter enum A { a, b } ;e enum B { b, a } ;porque eles estão em conflito.

Grzegorz
fonte
2
Até você usar enum classno C ++ 11, é isso. Antes disso, você precisa fazer aulas fictícias.
28412 chris
Não conhece o C ++ 11. Estou assumindo que a pergunta se refere ao C ++. Sim, o uso de classes ou namespaces fará o truque.
Grzegorz
@Grzegorz: Eu acho que Chris está se referindo à classe enum recém-introduzida, que fornece enumerações fortemente tipadas.
Rika
@Hossein: Obrigado por apontar. Encontrei explicações sobre a classe num e sei do que Chris estava falando. Muito obrigado.
Grzegorz
@Grzegorz: Eu não tive a intenção de desrespeito, apenas pensei que eu poderia estar ajudando, desculpe por qualquer misunderstanding.I provável vez obrigado pelo seu tempo e me ajudar;)
Rika
4

Enquanto o C ++ (excluindo o C ++ 11) possui enumerações, os valores neles são "vazados" no espaço para nome global.
Se você não deseja que eles vazem (e NÃO PRECISA usar o tipo de enum), considere o seguinte:

class EnumName {  
   public:   
      static int EnumVal1;  
      (more definitions)  
};  
EnumName::EnumVal1 = {value};  
if ([your value] == EnumName::EnumVal1)  ...
InitializeSahib
fonte
3

As enumerações em C ++ são como números inteiros mascarados pelos nomes que você atribui, quando você declara seus valores de enumeração (essa não é uma definição, apenas uma dica de como funciona).

Mas há dois erros no seu código:

  1. Soletre enumtodas as minúsculas
  2. Você não precisa do Days.sábado antes.
  3. Se esse enum for declarado em uma classe, use if (day == YourClass::Saturday){}
Balázs Édes
fonte
O OP mudou a ortografia / maiúscula 16 minutos após o post inicial ( revisão 1 para revisão 2 ).
Peter Mortensen
1

Eu acho que o seu problema de raiz é o uso de em .vez de ::, que usará o espaço para nome.

Experimentar:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Days::Saturday;
if(Days::Saturday == day)  // I like literals before variables :)
{
    std::cout<<"Ok its Saturday";
}
James Oravec
fonte
Isso não funciona: para usar o Days::escopo como no seu exemplo, você deve definir a enumeração enum class Dayse usar a extensão C ++ 03 + Microsoft ou C ++ 11.
Futal
@Futal, o acima foi executado com o Borland C ++ Builder. O sabor / versão do C ++ não está em questão.
James Oravec 10/09
1
sua versão do Borland C ++ Builder deve estar usando o C ++ 11 ou mais recente. O Gcc e o Clang emitem erros ou avisos se o seu exemplo for compilado com -std=c++98ou -std=c++03. Clang é bastante clara: warning: use of enumeration in a nested name specifier is a C++11 extension.
Futal 12/09/18
1

Se queremos o tipo estrito de segurança e o enum com escopo definido, o uso enum classé bom no C ++ 11.

Se tivéssemos que trabalhar no C ++ 98, podemos usar os conselhos de InitializeSahib, Sanpara habilitar a enumeração de escopo.

Se também queremos o tipo estrito de segurança, o código a seguir pode implementar algo parecido enum.

#include <iostream>
class Color
{
public:
    static Color RED()
    {
        return Color(0);
    }
    static Color BLUE()
    {
        return Color(1);
    }
    bool operator==(const Color &rhs) const
    {
        return this->value == rhs.value;
    }
    bool operator!=(const Color &rhs) const
    {
        return !(*this == rhs);
    }

private:
    explicit Color(int value_) : value(value_) {}
    int value;
};

int main()
{
    Color color = Color::RED();
    if (color == Color::RED())
    {
        std::cout << "red" << std::endl;
    }
    return 0;
}

O código é modificado a partir do exemplo Mês da classe no livro C ++ Efetivo 3º: Item 18

Xu Hui
fonte
-15

Primeiro, faça 'E' em enum, 'e' como minúscula.

Segundo, solte o nome do tipo 'Days' em 'Days.Saturday'.

Terceiro ... compre um bom livro em C ++.

vikramjitSingh
fonte
5
Desculpe, você recebeu todos esses votos negativos (quero dizer, a resposta realmente merece), mas isso não significa que você precise deixar a comunidade por 6 anos. Volte e junte-se a nós. Você também tem algo a contribuir. Seja útil. Compartilhe conhecimento.
Gabriel Staples