【发布时间】:2014-02-13 06:41:16
【问题描述】:
我有一个包含两个值的枚举类,我想创建一个接收值的方法 并返回另一个。我还想维护类型安全(这就是我使用枚举类而不是枚举的原因)。
http://www.cplusplus.com/doc/tutorial/other_data_types/ 没有提及任何有关方法的内容 但是,我的印象是任何类型的类都可以有方法。
【问题讨论】:
我有一个包含两个值的枚举类,我想创建一个接收值的方法 并返回另一个。我还想维护类型安全(这就是我使用枚举类而不是枚举的原因)。
http://www.cplusplus.com/doc/tutorial/other_data_types/ 没有提及任何有关方法的内容 但是,我的印象是任何类型的类都可以有方法。
【问题讨论】:
不,他们不能。
我可以理解 C++11 中强类型枚举的 enum class 部分似乎暗示您的 enum 也具有 class 特征,但事实并非如此。我有根据的猜测是,关键字的选择受到了我们在 C++11 之前用于获取作用域枚举的模式的启发:
class Foo {
public:
enum {BAR, BAZ};
};
但是,这只是语法。同样,enum class 不是 class。
【讨论】:
union 也不是 John Doe 认为的 class。然而它们可以有成员函数。并且类对于成员函数实际上不是强制性的。使用value 或this 之类的指示符,enum Size { Huge, Mega, Apocalypse; bool operator<(X rhs) const { return *this < rhs; } 之类的东西(这里也允许;),它与其他形式的函数一样有意义。
struct Foo{ enum {BAR, BAZ}; };,所以不是enum struct?
enum struct 实际上是创建作用域枚举的有效方法!谁知道!
虽然“您不能”的答案在技术上是正确的,但我相信您可以使用以下想法实现您正在寻找的行为:
我想你想写这样的东西:
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:
// Allow switch and comparisons.
constexpr operator Value() const { return value; }
// Prevent usage: if(fruit)
explicit operator bool() = delete;
#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();
可以支持。
【讨论】:
enum class?
专注于问题的描述而不是标题,可能的答案是
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 "";
}
};
【讨论】:
如other answer 中所述,没有。甚至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 specification 只支持更好地限定枚举值以进行访问。不多不少!
摆脱不能为枚举(类)声明方法的限制的方法是,使用std::bitset(包装类)或bitfield union。
unions,这样的位域联合可以有方法(限制见here!)。
我有一个示例,如何将位掩码值(如上所示)转换为相应的位索引,可以在此处与 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"
【讨论】:
enum ErrorType: int {
noConnection,
noMemory
};
class Error {
public:
Error() = default;
constexpr Error(ErrorType type) : type(type) { }
operator ErrorType() const { return type; }
constexpr bool operator == (Error error) const { return type == error.type; }
constexpr bool operator != (Error error) const { return type != error.type; }
constexpr bool operator == (ErrorType errorType) const { return type == errorType; }
constexpr bool operator != (ErrorType errorType) const { return type != errorType; }
String description() {
switch (type) {
case noConnection: return "no connection";
case noMemory: return "no memory";
default: return "undefined error";
}
}
private:
ErrorType type;
};
Error err = Error(noConnection);
err = noMemory;
print("1 " + err.description());
switch (err) {
case noConnection:
print("2 bad connection");
break;
case noMemory:
print("2 disk is full");
break;
default:
print("2 oops");
break;
}
if (err == noMemory) { print("3 Errors match"); }
if (err != noConnection) { print("4 Errors don't match"); }
【讨论】: