如何在C++中隐式地将枚举转换为它的子集,反之亦然?

cyej8jka  于 2023-06-07  发布在  其他
关注(0)|答案(2)|浏览(414)

更确切地说,我想要的功能是将枚举隐式地转换为它的子集枚举,反之亦然。
我希望它工作的代码:

enum class Human {
    A = 1,
    B = 2,
};

enum class Male {    // subset of Human
    A = Human::A,
};

enum class Female {    // subset of Human
    B = Human::B,
};

// some functions can handle all humans
void human_func(Human h) {
    // ...
}

// some only take a subset of humans
void male_func(Male m) {
    // ...
}

void female_func(Female m) {
    // ...
}

// and user only uses values of Human as token
constexpr auto SOMEONE = Human::A;

int main() {
    human_func(SOMEONE);  // ok
    male_func(SOMEONE);   // also ok, Human::A implicitly converted to Male
    female_func(SOMEONE); // failed, can't convert Human::A to Female.
}

但是enum不能进行转换。现在我有两个选择:

// 1. static_assert with template parameter

template <Human H>
void female_func() {
    static_assert(H == Human::B);
    // ...
}

// 2. manually convert it

#define _ENUM_TO_ENUM(e1, e2) \
    static_cast<e2>(static_cast<std::underlying_type_t<decltype(e1)>>(e1))

void female_func(_ENUM_TO_ENUM(SOMEONE, Female)) {
    // But this way the compiler does not check if the value is valid.
    // I can put anything in.
    // ...
}

那么,还有其他的技术可以做到这一点吗?

ubbxdtey

ubbxdtey1#

我认为这是继承和多态的典型用例。
如果您考虑将ManWoman枚举改为类,两者都为多态Human类派生,并且只保留性别的枚举,那么您“希望工作”的代码肯定会工作。
以下是所需返工的示例:

enum class Gender {MALE, FEMALE};

struct Human
{
    Gender m_gender;
    Human(Gender g) : m_gender{g}
    {}
    virtual ~Human() = default;
};

struct Man : public Human
{
    Man() : Human{Gender::MALE}
    {}
};
struct Woman : public Human
{
    Woman() : Human(Gender::FEMALE)
    {}
};

现在你可以像下面这样写你的函数:

void human_func(const Human & h)
{
    //...
}
void man_func(const Man & m)
{
    //...
}
void woman_func(const Woman & w)
{
    //...
}

并按以下方式使用它们:

int main()
{
    Man man;

    human_func(man);  // OK --> Human
    man_func(man);    // OK --> Man
    //woman_func(man);   NOK (invalid: a Man is not a Woman)

    return 0;
}

它工作的原因是,对于继承,ManHumanWoman也是如此。

u3r8eeie

u3r8eeie2#

C++中的enum class不能与另一个enum class有任何类型的is a关系,至少在语言级别上是这样。
您可以使用标准类型层次结构来实现您想要的功能,例如Maleis aHumanFemaleis aHuman,如示例代码所示。虽然,这可能是你正在寻找的相反。

示例代码

#include <iostream>

using std::cin, std::cout, std::endl;

struct Human {
};

struct Male : public Human {
};

struct Female : public Human {
};

// some functions can handle all humans
void human_func(Human& h) {
    // ...
}

// some only take a subset of humans
void male_func(Male& m) {
    // ...
}

void female_func(Female& m) {
    // ...
}

// and user only uses values of Human as token

int main() {
    auto human = Human{};
    human_func(human);
    // male_func(human); This would not compile
    // female_func(human); This would not compile

    auto male = Male{};
    human_func(male);
    male_func(male);
    // female_func(male); This would not compile

    auto female = Female{};
    human_func(female);
    // male_func(female); This would not compile
    female_func(female);
}

相关问题