如何将附加信息捆绑到枚举中
How can I bundle additional information to an enum?
假设我有一个关于我正在开发的游戏的状态枚举。首先,我有这样的内容:
enum class Status {
OK, HURT, DYING, DEAD
};
这一切都很好,但现在我想打印状态的名称。当然,如果这是一个普通的类,我会在Status
的实例上调用getName()
。但是,对于枚举不存在这样的选项。
解决这个问题的一个方法是像这样:
const char * getName(Status status) {
switch(status) {
case Status::OK:
return "OK";
break;
/*and so on */
}
然而,这显然不是很可扩展。当您有大量的枚举和大量的数据绑定在一起时,这就成了一个问题。你也没有以一种非常有意义的方式将相关数据联系在一起。使用统一的调用语法,您可以得到类似这样的结果:
Status::OK.getName();
但这还不是标准。
另一种解决这个问题的方法是在类中使用static const
成员,如下所示:
class Status {
std::string name;
Status(std::string name):name(std::move(name)) {}
public:
const std::string & getName() const { return name; }
static const Status OK, HURT, DYING, DEAD;
};
//in some .cpp
const Status Status::OK("OK"), Status::HURT("Hurt"), Status::DYING("Dying"), Status::DEAD("Dead");
这一切都很好,而且在一段时间内也很好。但是现在,我想写一些代码来处理每个Status
,所以本能地我更喜欢switch
而不是if
- else
链。
我这样写:
Status status = getStatus();
switch(status) {
case Status::OK:
//do something
default:
//do something else
}
当然,这不起作用。我需要一个转换操作符!所以我加上
operator int() { return /*some sort of unique value*/; }
…还是什么都没有。原因是操作符必须是constexpr
。这是显而易见的,知道switch
的细节,但它并没有真正的帮助。因此,下一个合乎逻辑的步骤是将static const
s变为static constexpr
s。
class Status {
const char * name; //we are in constexpr land now
constexpr Status(const char * name):name(name) {}
public:
constexpr const char * getName() const { return name; }
constexpr operator int() const { return /*some sort of unique value*/; }
static constexpr Status OK = Status("OK"),
HURT = Status("Hurt"),
DYING = Status("Dying"),
DEAD = Status("Dead");
};
//in some .cpp
constexpr Status Status::OK, Status::HURT, Status::DYING, Status::DEAD;
必须放置static
s的原因是constexpr
必须当场初始化。这将工作得很好,老实说,它涵盖了我所有的需求。唯一的问题是它不能编译。这是有道理的:一个尚未定义的类如何被初始化?这对const
来说不是问题,因为它们在实例化时是延迟的。
所以,现在唯一合理的方法是通过另一个类,这样当我们创建constexpr
s时,Status
的大小是已知的。
class Status {
const char * name;
constexpr Status(const char * name):name(name) {}
public:
constexpr const char * getName() const { return name; }
constexpr operator int() const { return /*some sort of unique value*/; }
struct constants;
};
struct Status::constants {
static constexpr Status OK = Status("OK"),
HURT = Status("Hurt"),
DYING = Status("Dying"),
DEAD = Status("Dead");
};
//in some .cpp
constexpr Status Status::constants::OK, Status::constants::HURT, Status::constants::DYING, Status::constants::DEAD;
这与Status::NAME
的自然语法有一段距离,而不得不说Status::constants::NAME
之类的东西。另一个问题是operator int()
。(据我所知)没有办法让它像enum那样被填充。在static const
实现中,明显的选择是使用私有的static int
,将其在actor中自增,并将其存储,使用它作为转换操作符的返回值。然而,这在constexpr
版本中不再是一个选项。也许有某种模板魔法可以做到这一点,如果是这样的话,我很想看到它。所以我的问题是,是否有任何改进要做使用现代c++的方式,我们捆绑信息到枚举?
根据Columbo的回答:当一个类的定义被传递给基类时,它就完成了吗?,可以实现扩展枚举。
template<class T>
class Constants {
public:
static const T OK, HURT, DYING, DEAD;
};
template <typename T>
constexpr T Constants<T>::OK = T("OK");
template <typename T>
constexpr T Constants<T>::HURT = T("Hurt");
template <typename T>
constexpr T Constants<T>::DYING = T("Dying");
template <typename T>
constexpr T Constants<T>::DEAD = T("Dead");
class Status : public Constants<Status> {
friend class Constants<Status>;
const char * name;
constexpr Status(const char * name):name(name) {}
public:
constexpr const char * getName() const { return name; }
constexpr operator int() const { return /*some sort of unique value*/; }
};
虽然这个实现仍然存在一些问题,例如缺乏返回唯一值(可能只能让actor获取一个值并手动执行),但它看起来相当不错。但请注意,这不会在MSVC(由于缺乏良好的constexpr
支持)或Clang(由于bug 24541)上编译。但是,嘿,这是目前为止我们得到的最好的。
- 不带大括号的枚举形式
- 枚举环境变量的惯用C++14/C++17方法
- 类似枚举的计算常量
- 如何正确实现和访问运算符的各种自定义枚举器
- 错误:从"int"到枚举c++的转换无效
- C++中构造函数中的枚举
- 访问在 C++ 结构中声明的枚举变量
- 枚举类'classname'的多重定义
- 强枚举类型定义:Clang Bug 还是 C++11 标准不确定性?
- typedef 枚举和枚举类有什么区别?
- 为什么我的开关/机箱在使用枚举时默认?
- 标准::可选枚举的比较运算符
- C++两个源文件之间共享的枚举的静态实例
- 打印没有铸件的枚举可以在C++中吗?
- 枚举成员与静态 int 成员?
- C++:枚举:错误:应使用标识符而不是"}"
- 带有 c++ 的枚举(输入检查)
- 在 qml 中使用 Q_ENUM 和 Q_PROPERTY 作为枚举类
- 为什么 int 类型的枚举类值不能用作 int
- 如何将附加信息捆绑到枚举中