C++ 枚举类可以有方法吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21295935/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can a C++ enum class have methods?
提问by octavian
I have an enum class with two values, and I want to create a method which receives a value and returns the other one. I also want to maintain type safety(that's why I use enum class instead of enums).
我有一个带有两个值的枚举类,我想创建一个接收一个值并返回另一个值的方法。我还想保持类型安全(这就是我使用枚举类而不是枚举的原因)。
http://www.cplusplus.com/doc/tutorial/other_data_types/doesn't mention anything about methods However, I was under the impression that any type of class can have methods.
http://www.cplusplus.com/doc/tutorial/other_data_types/没有提到任何关于方法的内容但是,我的印象是任何类型的类都可以有方法。
采纳答案by Stefano Sanfilippo
No, they can't.
不,他们不能。
I can understand that the enum class
part for strongly typed enums in C++11 might seem to imply that your enum
has class
traits too, but it's not the case. My educated guess is that the choice of the keywords was inspired by the pattern we used before C++11 to get scoped enums:
我可以理解,enum class
C++11 中强类型枚举的部分似乎暗示您enum
也具有class
特征,但事实并非如此。我有根据的猜测是,关键字的选择受到了我们在 C++11 之前用于获取作用域枚举的模式的启发:
class Foo {
public:
enum {BAR, BAZ};
};
However, that's just syntax. Again, enum class
is not a class
.
然而,这只是语法。再次,enum class
不是class
.
回答by jtlim
While the answer that "you can't" is technically correct, I believe you may be able to achieve the behavior you're looking for using the following idea:
虽然“你不能”的答案在技术上是正确的,但我相信你可以使用以下想法实现你正在寻找的行为:
I imagine that you want to write something like:
我想你想写这样的东西:
Fruit f = Fruit::Strawberry;
f.IsYellow();
And you were hoping that the code looks something like this:
你希望代码看起来像这样:
enum class Fruit : uint8_t
{
Apple,
Pear,
Banana,
Strawberry,
bool IsYellow() { return this == Banana; }
};
...
But of course, it doesn't work, because enums can't have methods (and 'this' doesn't mean anything in the above context)
但当然,它不起作用,因为枚举不能有方法(并且“this”在上述上下文中没有任何意义)
However, if you use the idea of a normal class containing a non-class enum and a single member variable that contains a value of that type, you can get extremely close to the syntax/behavior/type safety that you want. i.e.:
但是,如果您使用包含非类枚举和包含该类型值的单个成员变量的普通类的想法,您可以非常接近您想要的语法/行为/类型安全。IE:
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;
};
Now you can write:
现在你可以写:
Fruit f = Fruit::Strawberry;
f.IsYellow();
And the compiler will prevent things like:
编译器会阻止类似的事情:
Fruit f = 1; // Compile time error.
You could easily add methods such that:
您可以轻松添加以下方法:
Fruit f("Apple");
and
和
f.ToString();
can be supported.
可以支持。
回答by Márkus Attila
Concentrating on the description of the question instead of the title a possible answer is
专注于问题的描述而不是标题,可能的答案是
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 "";
}
};
回答by Konchog
There is a pretty compatible ability(§) to refactor an enum into a class without having to rewrite your code, which means that effectively you cando what you were asking to do without too much editing.
有一种非常兼容的能力(§) 可以将枚举重构为一个类,而无需重写代码,这意味着您可以有效地完成您要求做的事情,而无需进行过多的编辑。
(§) as ElementW points out in a comment, type_traitsdependent code will not work, so e.g. one cannot use auto, etc. There may be some way of handling such stuff, but in the end one is converting an enum into a class, and it is always a mistake to subvert C++
(§)正如 ElementW 在评论中指出的那样,依赖于type_traits 的代码将不起作用,因此例如不能使用 auto 等。可能有某种方法可以处理这些东西,但最终将枚举转换为类,而且颠覆C++永远是错误的
the enum struct
and enum class
specifications are about scoping so not part of this.
在enum struct
和enum class
规格约作用域所以没有这部分。
Your original enum is e.g. 'pet' (this is as an example only!).
您的原始枚举是例如“宠物”(这仅作为示例!)。
enum pet {
fish, cat, dog, bird, rabbit, other
};
(1) You modify that to eg petEnum (so as to hide it from your existing code).
(1) 您将其修改为例如 petEnum(以便将其从您现有的代码中隐藏)。
enum petEnum {
fish, cat, dog, bird, rabbit, other
};
(2) You add a new class declaration below it (named with the original enum)
(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) You can now add whatever class methods you like to your pet class. eg. a string operator
(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!";
}
}
Now you can use eg std::cout...
现在你可以使用例如 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;
}
回答by π?ντα ?ε?
As mentioned in the other answer, no. Even enum class
isn't a class.
正如另一个答案中提到的,没有。甚至enum class
不是一个班级。
Usually the needto have methods for an enum
results from the reason that it's not a regular(just incrementing) enum, but kind of bitwise definition of values to be masked or need other bit-arithmetic operations:
通常需要有一个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;
Obviously one thinks of encapsulating the necessary operations to re-/set single/group of bits, by e.g. bit mask value or even bit index driven operations would be useful for manipulation of such a set of 'flags'.
显然,人们认为封装必要的操作以重新/设置单个/一组位,例如通过位掩码值或什至位索引驱动的操作对于操纵这样一组“标志”是有用的。
The c++11struct
/class
specificationjust supports better scoping of enum values for access. No more, no less!
在C ++ 11struct
/class
规范只支持用于接入枚举值的更好的范围界定。不多也不少!
Ways to get out of the restriction you cannot declare methods for enum (classes)are , either to use a std::bitset
(wrapper class), or a bitfield union
.
摆脱不能为枚举(类)声明方法的限制的方法是使用std::bitset
(包装类)或位域union
。
union
s, and such bitfield unions canhave methods (see herefor the restrictions!).
union
s,并且这样的位域联合可以有方法(有关限制,请参见此处!)。
I have a sample, how to convert bit mask values (as shown above) to their corresponding bit indices, that can be used along a std::bitset
here: BitIndexConverter.hpp
I've found this pretty useful for enhancing readability of some 'flag' decison based algorithms.
我有一个示例,如何将位掩码值(如上所示)转换为其相应的位索引,可以在std::bitset
这里使用:BitIndexConverter.hpp
我发现这对于增强某些基于“标志”决策的可读性非常有用算法。
回答by Johannes
It may not fulfill all your needs, but with non-member operators you can still have a lot of fun. For example:
它可能无法满足您的所有需求,但对于非会员运营商,您仍然可以享受很多乐趣。例如:
#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";
}
}
This allows code like
这允许像这样的代码
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"