我有一个带有两个值的枚举类,并且我想创建一个接收一个值并返回另一个值的方法。我还想保持类型安全(这就是为什么我使用enum类而不是enums的原因)。
http://www.cplusplus.com/doc/tutorial/other_data_types/没有提及任何方法,但是,我的印象是任何类型的类都可以具有方法。
我有一个带有两个值的枚举类,并且我想创建一个接收一个值并返回另一个值的方法。我还想保持类型安全(这就是为什么我使用enum类而不是enums的原因)。
http://www.cplusplus.com/doc/tutorial/other_data_types/没有提及任何方法,但是,我的印象是任何类型的类都可以具有方法。
Answers:
不,他们不能。
我可以理解,enum class
C ++ 11中用于强类型枚举的部分似乎暗示您enum
也具有class
特征,但事实并非如此。我的有根据的猜测是,关键字的选择受到了我们在C ++ 11之前用来获取范围枚举的模式的启发:
class Foo {
public:
enum {BAR, BAZ};
};
但是,这只是语法。再次,enum class
不是class
。
union
是不是李四会考虑一个类,太。但是它们可以具有成员函数。对于成员函数,类实际上不是必需的。使用类似value
或的指示符(类似this
,enum Size { Huge, Mega, Apocalypse; bool operator<(X rhs) const { return *this < rhs; }
此处也允许;
),它与其他形式的功能一样有意义。
尽管“您不能”的答案在技术上是正确的,但我相信您可以使用以下思路来实现所需的行为:
我想您想编写类似以下内容的内容:
Fruit f = Fruit::Strawberry;
f.IsYellow();
您希望代码看起来像这样:
enum class Fruit : uint8_t
{
Apple,
Pear,
Banana,
Strawberry,
bool IsYellow() { return this == Banana; }
};
...
但是,当然,这是行不通的,因为枚举不能包含方法(并且“ this”在上面的上下文中没有任何意义)
但是,如果使用包含非类枚举和包含该类型的值的单个成员变量的普通类的想法,则可以非常接近所需的语法/行为/类型安全性。即:
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 bool operator==(Fruit a) const { return value == a.value; }
constexpr bool 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();
可以支持。
专注于问题的描述而不是标题,可能的答案是
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 "";
}
};
就像其他答案中提到的那样。甚至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;
显然,有人认为通过例如位掩码值或什至由位索引驱动的操作来封装重新设置/设置单个/一组位的必要操作对于这种“标志”集合的操作将是有用的。
的 C ++ 11 struct
/ class
规范仅支持对枚举值进行更好的范围划分以进行访问。不多不少!
摆脱限制的方法是无法声明枚举(类)的方法是使用std::bitset
(包装器类)或bitfieldunion
。
union
,这样的位域联合可以具有方法(有关限制,请参见此处!)。
我有一个示例,如何将位掩码值(如上所示)转换为其相应的位索引,可以在std::bitset
此处使用:BitIndexConverter.hpp
我发现这对于增强某些基于“标志”决策的可读性非常有用算法。
有一个相当兼容的功能(§),可以将枚举重构为一个类而不必重写代码,这意味着您可以有效地完成所要求的操作而无需进行过多的编辑。
(§)正如ElementW在注释中指出的那样,依赖于type_traits的代码将不起作用,因此例如不能使用auto等。可能有一些处理此类问题的方法,但最终将枚举转换为类,颠覆C ++总是一个错误
在enum struct
和enum class
规格约作用域所以没有这部分。
您最初的枚举例如是“ pet”(仅作为示例!)。
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;
}
pet
/实例,无论是模板auto
,还是decltype
,这都会中断,因为您得到了一个petEnum
。
它可能无法满足您的所有需求,但是使用非成员运营商,您仍然可以享受很多乐趣。例如:
#include <iostream>
enum class security_level
{
none, low, medium, high
};
static bool operator!(security_level s) { return s == security_level::none; }
static security_level& operator++(security_level& s)
{
switch(s)
{
case security_level::none: s = security_level::low; break;
case security_level::low: s = security_level::medium; break;
case security_level::medium: s = security_level::high; break;
case security_level::high: break;
}
return s;
}
static std::ostream & operator<<(std::ostream &o, security_level s)
{
switch(s)
{
case security_level::none: return o << "none";
case security_level::low: return o << "low";
case security_level::medium: return o << "medium";
case security_level::high: return o << "high";
}
}
这允许像
security_level l = security_level::none;
if(!!l) { std::cout << "has a security level: " << l << std::endl; } // not reached
++++l;
if(!!l) { std::cout << "has a security level: " << l << std::endl; } // reached: "medium"