0

Можно ли вручную определить преобразование для класса enum?

Описание проблемы:

Я хочу определить каст для enum class, чтобы можно было использовать его в условиях, аналогично тому, как это делается для классов, через явное преобразование типов. Например, у меня есть следующий класс:

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

Можно ли сделать что-то аналогичное для enum class? Я ищу способ кастовать enum class в bool или, возможно, в другой тип данных. Если у вас есть идеи или примеры, как это можно реализовать, буду признателен за помощь!

3 ответ(ов)

0

Нет, это не так.

На самом деле enum class вовсе не является классом. Ключевое слово class использовано лишь для того, чтобы избежать полной переработки уже существующего кода, если бы неохваченный enum вдруг был бы заменён на охваченный enum. Поэтому комитет решил обозначить новые стили перечислений, добавив к ним class, так как это уже существующее ключевое слово и в C++ не могло быть перечисления с именем class. Они могли бы выбрать другое слово, но это не имело бы особого смысла.

Тем не менее, несмотря на наличие ключевого слова class, это всё равно обычные перечисления, в том смысле, что только перечисляемые значения (и, возможно, присвоенные им значения) разрешены внутри фигурных скобок.

0

Нет, но вы можете сделать обычный класс таким, чтобы он вел себя как класс перечислений, используя constexpr члены и конструкторы. Таким образом, вы сможете добавить все необходимые вспомогательные функции.


Доказательство того, что это может работать даже с 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;
    }
}

Доказательство того, что работа с switch не требует неявной конвертации в int:

#include <iostream>

/* псевдо-перечисление, совместимое с switch и неявно не конвертируемое в целочисленный тип */
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;
    }
    // ЭТО ВЫЗОВЕТ ОШИБКУ: int z = fe;
}

Таким образом, вы можете использовать обычный класс в качестве перечисления и при этом не терять возможности добавлять необходимые функциональные возможности.

0

В C++ нельзя определять операторы приведения для ненадлежащих типов и, конечно, нельзя создавать членские функции для перечислений. Поэтому я предлагаю использовать свободные функции для преобразования вашего перечисления в другие типы, так же, как вы бы реализовали операторы приведения.

Например:

bool TestToBool(enum_e val)
{
    return false;
}

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

Существует удобный способ связать перечисления с логическими значениями, который подразумевает разделение на два файла: .h и .cpp. Вот пример, который может вам помочь:

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)
{
    // реализуйте логику преобразования
}

Я не компилировал этот код, так что относитесь к нему с пониманием, если в нем есть ошибки 😃.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь