¿Es posible definir manualmente una conversión para una clase de enumeración?

7 minutos de lectura

avatar de usuario
Entidad Omnipotente

Normalmente, puede definir una conversión para una clase utilizando la siguiente sintaxis:

class Test {
public:
  explicit operator bool() { return false; }
};

¿Hay alguna manera de hacer esto o algo similar para un enum class?

  • Si hicieras eso, ¿cuál sería el punto de usar enum class ¿en absoluto? enum se puede declarar hacia adelante, dado un tipo subyacente, y tiene como ámbito el nombre de la enumeración. Si puede convertirlos libremente de/a enteros… ¿por qué usar un enum class ¿en absoluto? ¿Porque es nuevo?

    – Nicolás Bolas

    5 oct 2012 a las 20:31

  • Porque todavía no quiero tener el enum class convertir a un número entero, pero podría definirlo para poder convertirlo en un booly hacer que valores específicos se evalúen como verdaderos y otros como falsos.

    – Entidad Omnipotente

    5 oct 2012 a las 20:31


  • Tenga en cuenta que no existe tal cosa como un “reparto implícito”. Un molde es algo que escribes en tu código fuente. Le dice al compilador que haga una conversión. Entonces, un lanzamiento es una conversión explícita. El compilador también puede hacer algunas conversiones sin conversión; se conocen como “conversiones implícitas”.

    – Pete Becker

    5 oct 2012 a las 21:29

  • @NicolBolas Estaba buscando en Google aquí porque me hubiera gustado la conversión implícita a bool, donde una enumeración es ÉXITO y las otras son códigos de error. Obviamente, uno puede poner una comparación, pero con los nombres de las variables como estaban, no era necesario para mayor claridad.

    – Andrés Lázaro

    8 de noviembre de 2014 a las 0:01

  • Hmm… un caso sería un conjunto de banderas para operaciones bit a bit usando un campo de bits de estilo C, @NicolBolas. Para una clase, por ejemplo, MyCBitfieldpermitiría al programador definir la conversión como static_cast<MyCBitfield::underlying_type>(1) << bitflag al tiempo que impide la conversión a int. (Aunque, en ese punto, es mejor que haga que el tipo sea construible a partir de la enumeración o proporcione un operador de asignación).

    – Justin Time – Reincorporar a Monica

    17 de agosto de 2019 a las 1:06


No, no es.

En realidad, un enum class no es ninguna clase en absoluto. los class la palabra clave solo se usa porque de repente cambia el ámbito sin ámbito enum a un alcance enum habría significado volver a trabajar todos enumera códigos. Entonces, el comité decidió que para distinguir entre enumeraciones de estilo nuevo y estilo antiguo, las nuevas se etiquetarían con classporque ya es una palabra clave, así que no enum podría haber sido nombrado class en C++. Podrían haber elegido otro, no habría tenido mucho más sentido de todos modos.

Sin embargo, a pesar de la class palabra clave siguen siendo enumeraciones regulares en el sentido de que solo los enumeradores (y potencialmente los valores asignados a ellos) están permitidos entre paréntesis.

  • enum class fue utilizado por primera vez con casi la misma semántica por C++/CLI. El Estándar adoptó la sintaxis existente y la estandarizó.

    – Ben Voigt

    5/10/2012 a las 22:00

  • Tiene sentido. Lástima que C++/CLI no se haya usado enum namespacePero es lo que es.

    – Eljay

    14/11/2017 a las 18:00

avatar de usuario
ben voigt

No, pero puede hacer que un tipo de clase normal actúe como una clase de enumeración, usando constexpr miembros y constructores. Y luego puede agregar todas las funciones de miembros adicionales que desee.


Prueba de que puede funcionar incluso con switch:

#include <iostream>

struct FakeEnum
{
    int x;

    constexpr FakeEnum(int y = 0) : x(y) {}

    constexpr operator int() const { return x; }

    static const FakeEnum A, B, Z;
};

constexpr const FakeEnum FakeEnum::A{1}, FakeEnum::B{2}, FakeEnum::Z{26};

std::istream& operator>>(std::istream& st, FakeEnum& fe)
{
    int val;
    st >> val;
    fe = FakeEnum{val};
    return st;
}

int main()
{
    std::cout << "Hello, world!\n";
    FakeEnum fe;
    std::cin >> fe;

    switch (fe)
    {
        case FakeEnum::A:
        std::cout << "A\n";
        break;
        case FakeEnum::B:
        std::cout << "B\n";
        break;
        case FakeEnum::Z:
        std::cout << "Z\n";
        break;
    }
}

Prueba de que trabajar con switch no requiere interconversión implícita con int:

#include <iostream>

/* pseudo-enum compatible with switch and not implicitly convertible to integral type */
struct FakeEnum
{
    enum class Values { A = 1, B = 2, Z = 26 };
    Values x;

    explicit constexpr FakeEnum(int y = 0) : FakeEnum{static_cast<Values>(y)} {}
    constexpr FakeEnum(Values y) : x(y) {}

    constexpr operator Values() const { return x; }
    explicit constexpr operator bool() const { return x == Values::Z; }

    static const FakeEnum A, B, Z;
};

constexpr const FakeEnum FakeEnum::A{Values::A}, FakeEnum::B{Values::B}, FakeEnum::Z{Values::Z};

std::istream& operator>>(std::istream& st, FakeEnum& fe)
{
    int val;
    st >> val;
    fe = FakeEnum(val);
    return st;
}

int main()
{
    std::cout << "Hello, world!\n";
    FakeEnum fe;
    std::cin >> fe;

    switch (fe)
    {
        case FakeEnum::A:
        std::cout << "A\n";
        break;
        case FakeEnum::B:
        std::cout << "B\n";
        break;
        case FakeEnum::Z:
        std::cout << "Z\n";
        break;
    }
    // THIS ERRORS: int z = fe;
}

  • Boost es un buen ejemplo de esto: mire la parte para no C++ 11 (BOOST_NO_SCOPED_ENUMS está definido) y extender eso — boost.org/doc/libs/1_50_0/boost/detail/…

    – Travis Gockel

    5 oct 2012 a las 22:15

  • “puedes hacer que un tipo de clase normal actúe como una clase de enumeración”: desafortunadamente, no poderen C++, porque C++’ switch es estúpido (también es difícil/imposible convertirlo en un POD mientras se conservan invariantes cuerdos, como un constructor privado).

    – Konrad Rodolfo

    13 de febrero de 2018 a las 18:22

  • C++ se esfuerza por proporcionar funciones de alto nivel al mismo tiempo que mantiene el acceso directo a la máquina (“mejor lenguaje ensamblador”). Los diseñadores han dicho muchos veces, que C++ es multi-paradigma. switch es acceso directo a una tabla de salto, exactamente igual que en C. Y un lenguaje de programación de sistemas debería tener acceso directo. Creo que sería genial si algún día C ++ agregara algunos operadores de coincidencia de patrones, pero sería un error horrible si usaran el switch palabra clave para eso.

    – Ben Voigt

    11 de abril de 2019 a las 23:44

  • @KonradRudolph: tenga en cuenta que los valores A, B, Zen realidad no tiene que existir en el tipo de clase de enumeración secreta… se garantiza (a diferencia de una enumeración heredada) que un objeto de clase de enumeración puede almacenar el mismo conjunto de valores que su tipo subyacente, ya sea que sean parte o no del cierre de enumeradores bajo bitwise-OR.

    – Ben Voigt

    12 abr 2019 a las 14:00

  • @BenVoigt Veo a dónde vas con esto. De hecho, se puede hacer que CRTP funcione con esto.

    – Konrad Rodolfo

    12 abr 2019 a las 15:30

avatar de usuario
soy real

No puede definir operadores de conversión que no sean miembros en C++. Y ciertamente no puede definir funciones miembro para enumeraciones. Así que le sugiero que haga funciones gratuitas para convertir su enumeración a otros tipos, de la misma manera que implementaría operadores de conversión.

p.ej

bool TestToBool(enum_e val)
{
    return false;
}

const char *TestToString(enum_e val)
{
    return "false";
}

Hay una buena manera de asociar esas enumeraciones a bools, tienes que dividirlas en dos archivos .h y .cpp. Aquí está si ayuda:

enum.h

///////////////////////////////
// enum.h
#ifdef CPP_FILE
#define ENUMBOOL_ENTRY(A, B)            { (enum_e) A, (bool) B },
struct EnumBool
{
    enum_e  enumVal;
    bool    boolVal;
};
#else
#define ENUMBOOL_ENTRY(A, B)            A,
#endif


#ifdef CPP_FILE
static EnumBool enumBoolTable[] = {
#else
enum enum_e
{
#endif
ENUMBOOL_ENTRY(ItemA, true),
ENUMBOOL_ENTRY(ItemB, false),
...
};

bool EnumToBool(enum_e val);

enum.cpp

///////////////////////////////
// enum.cpp
#define CPP_FILE
#include "enum.h"

bool EnumToBool(enum_e val)
    //implement

No lo compilé, así que tómatelo con calma si tiene algún error :).

  • FYI: No puedes tener ambos. Solo uno o el otro.

    – Nicolás Bolas

    5 oct 2012 a las 20:37

  • quisquilloso: TestToString debería estar devolviendo un const char*.

    – Ed S.

    5 oct 2012 a las 20:39

  • quisquilloso: TestToString debería estar regresando std::string

    – jwm

    12 de abril de 2019 a las 0:48

¿Ha sido útil esta solución?