我们说我已经列举了我正在进行的游戏的状态。起初,我有这样的事情:
enum class Status {
OK, HURT, DYING, DEAD
};
这一切都很好,花花公子,但现在我想要打印状态的名称。当然,如果这是一个普通的课程,我会在getName()
的实例上调用Status
。但是,枚举不存在这样的选项。
解决此问题的方法是使用以下方法:
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
成为static constexpr
。
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
的问题,因为它们在实例化时会延迟。
所以现在,做这样的事情的唯一合理方法是通过另一个类,以便在创建Status
时知道constexpr
的大小。
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()
。没有办法(我知道)以一种像枚举的方式填充它。在static const
实现中,显而易见的选择是拥有私有static int
,在ctor中递增它,并将其存储,使用它作为转换运算符的返回值。但是,这不再是constexpr
版本中的选项。也许有某种模板魔法可以做到,如果是这样,我很乐意看到它。所以我的问题是,使用现代C ++对我们将信息捆绑到枚举的方式有什么改进吗?
答案 0 :(得分:0)
根据Columbo在这里的回答:Is a class definition complete when it is passed to a base class?,可以实现扩展的枚举。
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*/; }
};
虽然这个实现仍然存在一些问题,例如缺少返回一个唯一值(可能只需要让ctor取一个并手动完成),它看起来相当不错。但请注意,这不会在MSVC上编译(由于缺少良好的constexpr
支持)或Clang(由于bug 24541)。但是,嘿,这是我们到目前为止最好的。