Может ли класс перечислимого класса С++ иметь методы?
У меня есть класс enum с двумя значениями, и я хочу создать метод, который получает значение
и возвращает другую. Я также хочу сохранить безопасность типов (поэтому я использую класс enum вместо перечислений).
http://www.cplusplus.com/doc/tutorial/other_data_types/ ничего не упоминает о методах
Однако у меня создалось впечатление, что любой тип класса может иметь методы.
Ответы
Ответ 1
Нет, они не могут.
Я могу понять, что часть enum class
для строго типизированных перечислений в С++ 11 может показаться, что ваше enum
имеет черты class
, но это не так. Мое обоснованное предположение состоит в том, что выбор ключевых слов был основан на шаблоне, который мы использовали до С++ 11 для получения перечислений с определенными областями видимости:
class Foo {
public:
enum {BAR, BAZ};
};
Однако это всего лишь синтаксис. И снова, enum class
- это не class
.
Ответ 2
Хотя ответ "вы не можете" технически верен, я полагаю, что вы, возможно, сможете достичь желаемого поведения, используя следующую идею:
Я представляю, что вы хотите написать что-то вроде:
Fruit f = Fruit::Strawberry;
f.IsYellow();
И вы надеялись, что код выглядит примерно так:
enum class Fruit : uint8_t
{
Apple,
Pear,
Banana,
Strawberry,
bool IsYellow() { return this == Banana; }
};
...
Но, конечно, это не работает, потому что у перечислений не может быть методов (и "это" ничего не значит в приведенном выше контексте)
Однако, если вы используете идею обычного класса, содержащего перечисление, не относящееся к классу, и одну переменную-член, которая содержит значение этого типа, вы можете быть очень близки к необходимой безопасности синтаксиса/поведения/типа. то есть:
class Fruit
{
public:
enum Value : uint8_t
{
Apple,
Pear,
Banana,
Strawberry
};
Fruit() = default;
constexpr Fruit(Value aFruit) : value(aFruit) { }
#if Enable switch(fruit) use case:
operator Value() const { return value; } // Allow switch and comparisons.
// note: Putting constexpr here causes
// clang to stop warning on incomplete
// case handling.
explicit operator bool() = delete; // Prevent usage: if(fruit)
#else
constexpr operator==(Fruit a) const { return value == a.value; }
constexpr operator!=(Fruit a) const { return value != a.value; }
#endif
constexpr bool IsYellow() const { return value == Banana; }
private:
Value value;
};
Теперь вы можете написать:
Fruit f = Fruit::Strawberry;
f.IsYellow();
И компилятор предотвратит такие вещи, как:
Fruit f = 1; // Compile time error.
Вы можете легко добавить такие методы, которые:
Fruit f("Apple");
а также
f.ToString();
может быть поддержано.
Ответ 3
Сосредоточив внимание на описании вопроса, а не на названии, можно ответить на вопрос
struct LowLevelMouseEvent {
enum Enum {
mouse_event_uninitialized = -2000000000, // generate crash if try to use it uninitialized.
mouse_event_unknown = 0,
mouse_event_unimplemented,
mouse_event_unnecessary,
mouse_event_move,
mouse_event_left_down,
mouse_event_left_up,
mouse_event_right_down,
mouse_event_right_up,
mouse_event_middle_down,
mouse_event_middle_up,
mouse_event_wheel
};
static const char* ToStr (const type::LowLevelMouseEvent::Enum& event)
{
switch (event) {
case mouse_event_unknown: return "unknown";
case mouse_event_unimplemented: return "unimplemented";
case mouse_event_unnecessary: return "unnecessary";
case mouse_event_move: return "move";
case mouse_event_left_down: return "left down";
case mouse_event_left_up: return "left up";
case mouse_event_right_down: return "right down";
case mouse_event_right_up: return "right up";
case mouse_event_middle_down: return "middle down";
case mouse_event_middle_up: return "middle up";
case mouse_event_wheel: return "wheel";
default:
Assert (false);
break;
}
return "";
}
};
Ответ 4
Как уже упоминалось в другом ответе, нет. Даже enum class
не является классом.
Обычно требуется иметь методы для enum
, потому что это не регулярное (просто увеличивающееся) перечисление, а вид поразрядного определения значений, которые нужно маскировать, или нужен другой бит -арифметические операции:
enum class Flags : unsigned char {
Flag1 = 0x01 , // Bit #0
Flag2 = 0x02 , // Bit #1
Flag3 = 0x04 , // Bit #3
// aso ...
}
// Sets both lower bits
unsigned char flags = (unsigned char)(Flags::Flag1 | Flags::Flag2);
// Set Flag3
flags |= Flags::Flag3;
// Reset Flag2
flags &= ~Flags::Flag2;
Очевидно, каждый думает о том, чтобы инкапсулировать необходимые операции для повторного набора/группировки битов, например, битовая маска или даже операции с битовым индексом были бы полезны для манипулирования таким набором "флагов".
С++ 11 struct
/class
спецификация просто поддерживает лучшее определение значений перечисления для доступа. Не больше, не меньше!
Способы выйти из ограничения, которое вы не можете объявить методы для перечисления (классов), либо использовать std::bitset
(класс оболочки) или битполе union
.
union
s, и такие объединения битовых полей могут иметь методы (см. здесь для ограничений!).
У меня есть образец, как преобразовать значения битовой маски (как показано выше) в соответствующие им битовые индексы, которые можно использовать по std::bitset
здесь: BitIndexConverter.hpp
Я нашел это довольно полезным для повышения удобочитаемости некоторых алгоритмов, основанных на определении "флаг".
Ответ 5
Существует довольно совместимая возможность (§) реорганизовать перечисление в класс без необходимости переписывать ваш код, что означает, что вы можете эффективно выполнять то, что вы просили, без слишком большого редактирования.
(§) как указывает ElementW в комментарии, зависимый от type_traits код работать не будет, поэтому, например, нельзя использовать auto и т.д. Может быть какой-то способ обработки таких вещей, но в итоге можно преобразовать перечисление в класс, и это всегда ошибка подорвать C++
спецификации enum struct
и enum class
относятся к области видимости, поэтому не являются частью этого.
Ваше оригинальное перечисление, например, "домашнее животное" (это только для примера!).
enum pet {
fish, cat, dog, bird, rabbit, other
};
(1) Вы изменяете это, например, на petEnum (чтобы скрыть его от существующего кода).
enum petEnum {
fish, cat, dog, bird, rabbit, other
};
(2) Вы добавляете новое объявление класса под ним (названное с оригинальным перечислением)
class pet {
private:
petEnum value;
pet() {}
public:
pet(const petEnum& v) : value{v} {} //not explicit here.
operator petEnum() const { return value; }
pet& operator=(petEnum v) { value = v; return *this;}
bool operator==(const petEnum v) const { return value == v; }
bool operator!=(const petEnum v) const { return value != v; }
// operator std::string() const;
};
(3) Теперь вы можете добавлять любые классовые методы, которые вам нравятся, в ваш класс питомца например. оператор строки
pet::operator std::string() const {
switch (value) {
case fish: return "fish";
case cat: return "cat";
case dog: return "dog";
case bird: return "bird";
case rabbit: return "rabbit";
case other: return "Wow. How exotic of you!";
}
}
Теперь вы можете использовать, например, std :: cout...
int main() {
pet myPet = rabbit;
if(myPet != fish) {
cout << "No splashing! ";
}
std::cout << "I have a " << std::string(myPet) << std::endl;
return 0;
}
Ответ 6
@jtlim Мне жаль, что это не сработает для меня
constexpr Fruit(Value aFruit) : fruit(aFruit) { }
Я получаю предупреждение и ошибку:
W: Use of this statement in a constexpr constructor is a C++14 extension
E: Constexpr constructor must initialize all members