如何将其他信息捆绑到枚举中?

时间:2015-10-21 15:14:09

标签: c++ c++11

我们说我已经列举了我正在进行的游戏的状态。起初,我有这样的事情:

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 ++对我们将信息捆绑到枚举的方式有什么改进吗?

1 个答案:

答案 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)。但是,嘿,这是我们到目前为止最好的。