Cómo usar enumeraciones en C ++

218

Supongamos que tenemos enumlo siguiente:

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

Quiero crear una instancia de esto enume inicializarlo con un valor adecuado, así que hago:

Days day = Days.Saturday;

Ahora quiero verificar mi variable o instancia con un enumvalor existente , así que hago:

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

Lo que me da un error de compilación:

error: expresión primaria esperada antes de '.' simbólico

Para que quede claro, ¿cuál es la diferencia entre decir:

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

y

if (day == Saturday)

?

¿A qué se refieren realmente estos dos, en que uno está bien y uno causa un error de compilación?

Rika
fuente
44
¡Lo sé, quiero saber por qué me está dando el error!
Rika
1
Es miércoles aquí. Tiene demasiados errores de sintaxis para el compilador de C ++. A partir de 'Enum'.
Öö Tiib
1
@Hossein, porque las enumeraciones no son la misma sintaxis (y semántica) en ambos idiomas. Lo primero que hago después de recibir un error al intentar usar una función en un nuevo idioma es buscar la sintaxis (o si es posible) en ese idioma.
Chris
@chris: Lo sé, hago exactamente lo mismo. Afortunadamente recibí mi respuesta. También actualicé la pregunta para ser más clara. Gracias por cierto;)
Rika
17
" Hasta donde yo sé, la declaración de enumeraciones y el uso en estos dos idiomas son los mismos ". Ahí está tu problema, ahí mismo. C # no es el mismo lenguaje que C ++. En particular, tienen una sintaxis diferente para las enumeraciones.
Robᵩ

Respuestas:

350

Este código está mal:

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

Porque Daysno es un alcance, ni un objeto. Es un tipo. Y los Tipos en sí mismos no tienen miembros. Lo que escribiste es el equivalente a std::string.clear. std::stringes un tipo, por lo que no puede usarlo .. Usas .en una instancia de una clase.

Desafortunadamente, las enumeraciones son mágicas, por lo que la analogía se detiene allí. Porque con una clase, puede hacer std::string::clearpara obtener un puntero a la función miembro, pero en C ++ 03, Days::Sundayno es válido. (Lo cual es triste) Esto se debe a que C ++ es (algo) retrocompatible con C, y C no tenía espacios de nombres, por lo que las enumeraciones tenían que estar en el espacio de nombres global. Entonces la sintaxis es simplemente:

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

Afortunadamente, Mike Seymour observa que esto se ha abordado en C ++ 11. Cambiar enuma enum classy que llega a su propio ámbito de aplicación; así Days::Sundayque no solo es válido, sino que es la única forma de acceder Sunday. ¡Días felices!

Pato mugido
fuente
254
Afortunadamente, su queja se ha abordado en C ++ 11. Cambiar enuma enum classy que llega a su propio ámbito de aplicación; así Days::Sundayque no solo es válido, sino que es la única forma de acceder Sunday. ¡Días felices!
Mike Seymour
11
Tengo que amar los mensajes de error de C ++ ... prueban que el lenguaje es engorroso para incluso dar buenos comentarios. Supongo que una 'expresión primaria' es un objeto o un alcance o alguna otra cosa que NO es un tipo. Quizás un tipo es una 'expresión secundaria'. Y lo que un desarrollador de C ++ podría llamar un 'operador de punto', el compilador de C ++ solo puede llamar un 'token'. Cuando se hace difícil entender los mensajes de error, creo que hay algo mal con el lenguaje.
Travis
44
@Travis: en.cppreference.com/w/cpp/language/… . Una expresión primaria es solo lo primero en una expresión, generalmente un nombre o variable o un literal. En cuanto a la segunda parte, no veo una gran diferencia entre '.' tokeny dot operator, aparte de que es un token y no un operador, y muestra el símbolo exacto, en lugar de un nombre.
Mooing Duck
@ Mike Seymour He intentado acceder a las enumeraciones sin los operadores de resolución de alcance en un grupo de compiladores, y parece funcionar. Usted dijo que a partir de C ++ 11 es la única forma, por alguna razón puedo acceder a los valores de enumeración como globales, no necesito el ::
Zebrafish
1
@TitoneMaurice: si tiene un enum, no puede usar ningún alcance o el alcance global ( ::Saturday). Si tiene un enum class(que es una cosa muy diferente), entonces tiene que usar Days::Saturday.
Mooing Duck
24

Esto será suficiente para declarar su variable enum y compararla:

enum Days {Saturday, Sunday, Tuesday, Wednesday, Thursday, Friday};
Days day = Saturday;
if (day == Saturday) {
    std::cout << "Ok its Saturday";
}
matemático1975
fuente
¿Por qué está mal decir if (day == Days.Satudday)? deben ser iguales, entonces, ¿por qué el compilador se queja de eso?
Rika
1
@Hossein los valores declarados en su enumeración no se comportan como variables miembro de clase o estructura. Esta no es la sintaxis correcta para usar
mathematician1975
2
@Hossein: porque Daysno es un alcance, ni un objeto. Es un tipo. Y los Tipos en sí mismos no tienen miembros. std::string.clearTampoco se compila por el mismo motivo.
Mooing Duck
8
@Hossein: Porque no es así como funcionan las enumeraciones en C ++. Las enumeraciones sin ámbito colocan sus valores en el espacio de nombres circundante; los Scoped ( enum class, nuevos en 2011) tienen su propio ámbito de aplicación, y se puede acceder mediante el operador alcance Days::Saturday. El operador de acceso de miembros ( .) solo se usa para acceder a los miembros de la clase.
Mike Seymour
@MooingDUck y MikeSeymour ¿Uno de ustedes publicaría su respuesta como respuesta? porque eso es exactamente lo que buscaba al emitir esta pregunta;)
Rika
22

Gran parte de esto debería darte errores de compilación.

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

Ahora, Saturday, Sunday, etc. puede ser utilizado como constantes desnudos de nivel superior, y Daysse puede utilizar como un tipo:

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

Y de manera similar más tarde, para probar:

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

Estos enumvalores son como constantes desnudas, sin ámbito, con un poco de ayuda adicional del compilador: (a menos que esté usando clases de enumeración de C ++ 11 ) no están encapsulados como miembros de objetos o estructuras, por ejemplo, y no puedes referirte a ellos como miembros de Days.

Tendrá lo que está buscando con C ++ 11 , que presenta un enum class:

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

// ...

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

Tenga en cuenta que este C ++ es un poco diferente de C en un par de formas, una es que C requiere el uso de la enumpalabra clave al declarar una variable:

// day declaration in C:
enum Days day = Saturday;
pb2q
fuente
He actualizado la pregunta, creo que ahora está más claro lo que busco exactamente :) Por cierto, gracias :)
Rika
14

Puede usar un truco para usar los ámbitos como desee, simplemente declare enum de la siguiente manera:

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

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

En lugar de usar un montón de declaraciones if, las enumeraciones se prestan bien para cambiar las declaraciones

Utilizo algunas combinaciones de enum / switch en el generador de niveles que estoy construyendo para mi juego.

EDITAR: Otra cosa, veo que quieres una sintaxis similar a;

if(day == Days.Saturday)
etc

Puedes hacer esto en C ++:

if(day == Days::Saturday)
etc

Aquí hay un ejemplo muy simple:

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
fuente
Lo bueno aquí es que los compiladores te dirán si te perdiste de poner un caso.
chris
¿No deberías usar la clase enum en este caso?
Rika
1
enum es solo un tipo de datos en C ++ Por lo tanto, declarar una enumeración como hice anteriormente en un archivo .h, y luego incluir ese archivo en cualquier archivo .cpp en el que desee usar le dará acceso a la enumeración. Acabo de notar que olvidé agregar el #include en mi ejemplo .cpp. Edición.
Dean Knight el
Además, veo a alguien más diciendo que las enumeraciones en C ++ son globales. En mi experiencia, usando las enumeraciones de la manera que lo hice anteriormente, solo puedo acceder a ellas cuando he incluido el .h. Así que esto también parece detener el acceso global, que siempre es bueno. EDITAR: Parece que sin saberlo estoy usando enumeraciones de una manera C ++ 11 si estoy leyendo las cosas bien ...
Dean Knight
9

Si todavía usa C ++ 03 y desea usar enumeraciones, debería usar enumeraciones dentro de un espacio de nombres. P.ej:

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

Puede usar la enumeración fuera del espacio de nombres como,

Daysofweek::Days day = Daysofweek::Saturday;

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

Está buscando enumeraciones fuertemente tipadas , una característica disponible en el estándar C ++ 11 . Convierte las enumeraciones en clases con valores de alcance.

Usando su propio ejemplo de código, es:

  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

El uso ::como accesores a las enumeraciones fallará si se dirige a un estándar C ++ anterior a C ++ 11. Pero algunos compiladores antiguos no lo admiten, y algunos IDE simplemente anulan esta opción y configuran un viejo estándar de C ++.

Si está utilizando GCC, habilite C + 11 con -std = c ++ 11 o -std = gnu11 .

¡Sea feliz!

Alex Byrth
fuente
1
Olvidaste escribir enum class Days { ....
Martin Hennings
En efecto. ¡arreglando lo! Gracias.
Alex Byrth
7

Esto no debería funcionar en C ++:

Days.Saturday

Days no es un ámbito u objeto que contenga miembros a los que pueda acceder con el operador de puntos. Esta sintaxis es solo un C # y no es legal en C ++.

Microsoft ha mantenido durante mucho tiempo una extensión C ++ que le permite acceder a los identificadores utilizando el operador de alcance:

enum E { A, B, C };

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

Pero esto no es estándar antes de C ++ 11. En C ++ 03, los identificadores declarados en una enumeración solo existen en el mismo ámbito que el tipo de enumeración en sí.

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

C ++ 11 hace legal calificar los identificadores enum con el nombre enum, y también introduce clases enum, que crean un nuevo alcance para los identificadores en lugar de colocarlos en el alcance circundante.

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

enum class F { A, B, C };

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

Lamentablemente, los elementos de la enumeración son 'globales'. Accede a ellos haciendo day = Saturday. Eso significa que no puede tener enum A { a, b } ;y enum B { b, a } ;porque están en conflicto.

Grzegorz
fuente
2
Hasta que lo use enum classen C ++ 11, eso es. Antes de eso, tienes que hacer clases falsas.
Chris
No sé C ++ 11. Supongo que la pregunta se refiere a C ++. Sí, usar clases o espacios de nombres hará el truco.
Grzegorz
@Grzegorz: creo que Chris se refiere a la nueva clase de enumeración que proporciona enumeraciones fuertemente tipadas.
Rika
@Hossein: Gracias por señalarlo. He encontrado una explicación de la clase num, y sé de qué estaba hablando Chris. Muchas gracias.
Grzegorz
@Grzegorz: No quise faltarle al respeto, solo pensé que podría estar ayudando, perdón por cualquier posible malentendido. Nuevamente, gracias por su tiempo y por ayudarme;)
Rika
4

Si bien C ++ (excluyendo C ++ 11) tiene enumeraciones, los valores en ellas se "filtraron" en el espacio de nombres global.
Si no desea que se filtren (y NO NECESITA usar el tipo de enumeración), considere lo siguiente:

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

Las enumeraciones en C ++ son como enteros enmascarados por los nombres que les da, cuando declara sus valores de enumeración (esto no es una definición, solo una pista de cómo funciona).

Pero hay dos errores en su código:

  1. Deletrear enumtodo en minúsculas
  2. No necesitas el Days.antes del sábado.
  3. Si esta enumeración se declara en una clase, use if (day == YourClass::Saturday){}
Balázs Édes
fuente
El OP cambió la ortografía / mayúscula 16 minutos después de la publicación inicial ( revisión 1 a revisión 2 ).
Peter Mortensen el
1

Creo que su problema raíz es el uso de en .lugar de ::, que utilizará el espacio de nombres.

Tratar:

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
fuente
Esto no funciona: para usar el Days::alcance como en su ejemplo, debe definir la enumeración enum class Daysy usar C ++ 03 + extensión de Microsoft o C ++ 11.
Futal
@Futal, lo anterior se ejecutó con Borland C ++ Builder. El sabor / versión de C ++ no está en la pregunta.
James Oravec
1
su versión de Borland C ++ Builder debe estar usando C ++ 11 o más reciente. Gcc y Clang ofrecen errores o advertencias si su ejemplo se compila con -std=c++98o -std=c++03. Sonido metálico es bastante claro: warning: use of enumeration in a nested name specifier is a C++11 extension.
Futal
1

Si queremos la seguridad estricta del tipo y la enumeración de ámbito, el uso enum classes bueno en C ++ 11.

Si tuviéramos que trabajar en C ++ 98, podemos usar los consejos dados por InitializeSahib, Sanpara habilitar la enumeración de ámbito.

Si también queremos la seguridad estricta del tipo, el siguiente código puede implementar algo así 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;
}

El código se modifica a partir del ejemplo de la clase Mes en el libro Efectivo C ++ 3er: Artículo 18

Xu Hui
fuente
-15

En primer lugar, escriba 'E' en enumeración, 'e' como minúscula.

Segundo, suelte el nombre de tipo 'Días' en 'Días.Sábado'.

Tercero ... cómprate un buen libro de C ++.

vikramjitSingh
fuente
55
Lamento que haya recibido todos estos votos negativos (es decir, la respuesta se lo merece), pero eso no significa que tenga que abandonar la comunidad durante 6 años. Regresa y únete a nosotros. Tú también tienes algo que aportar. Se útil Compartir conocimientos.
Gabriel Staples,