使用另一个枚举值的操作定义枚举元素值

3

这是我需要做的事情:在一个类内定义两个枚举,第二个枚举使用第一个枚举的元素值进行定义。

大概就像这样:

class MyClass
{
    public:
        enum class Elem {
            A=1, B=2, C=4, D=8
        };
        enum class Group {
            first = Elem::A | Elem::B,
            second = Elem::A | Elem::C,
            //...
        }; <-- compilation error
};

然而,由于默认情况下枚举类型中未定义 | 运算符,因此无法编译

我试图在 MyClass 类体外(类体后)为 Elem 枚举定义 | 运算符,但是在定义 Group 枚举时运算符是不可知的。

因此,我尝试了以下方法,即在类内部定义一个 constexpr 函数:

class MyClass
{
    public:
        enum class Elem {
            A=1, B=2, C=4, D=8
        };

        constexpr static unsigned int merge(
            std::initializer_list<MyClass::Elem> list)
        {
            //Test only: should be an '|' on all list elements
            return 1;
        }

        enum class Group {
            first = merge({Elem::A,Elem::B}),
            second = merge({Elem::A,Elem::C}),
            /*...*/
        };
};

但我收到了以下错误:

错误:在常量表达式中调用 static constexpr unsigned int merge(std::initializer_list list)。

我从这里那里了解到,只有在类被完全声明之后,merge方法才被认为是已声明且可用的。

我能想到的最后一个解决方案是使用宏,像这样:

#define MERGE2ELEMS( a, b ) static_cast<unsigned int>(a) | static_cast<unsigned int>(b)
#define MERGE3ELEMS( a, b, c ) static_cast<unsigned int>(a) | MERGE2ELEMS( b, c )
#define MERGE4ELEMS( a, b, c, d ) static_cast<unsigned int>(a) | MERGE3ELEMS( b, c, d )
#define MERGE5ELEMS( a, b, c, d, e ) static_cast<unsigned int>(a) | MERGE4ELEMS( b, c, d, e )
...

但我需要能够合并多达20个 Elem,编写20个类似这样的宏似乎并不是一个合适的解决方案。

在这种情况下,应该采取什么方法?


http://coliru.stacked-crooked.com/a/d3c5c6929fe0357e :( 我真的以为那会起作用。 - Mooing Duck
和我的merge方法一样的问题 :) - Silverspur
1
看起来有点荒谬。所有需要的类都已经完全声明了 :( - Mooing Duck
3个回答

2
您可以自由调整定义的顺序:
class MyClass {
   public:
    enum class Elem { A = 1, B = 2, C = 4, D = 8 };
    enum class Group;
};

constexpr MyClass::Elem operator|(
    const MyClass::Elem& l, const MyClass::Elem& r) {
    return static_cast<MyClass::Elem>(
              static_cast<int>(l) | static_cast<int>(r)
    );
}

enum class MyClass::Group {
    first = Elem::A | Elem::B,
    second = Elem::A | Elem::C,

};

演示现场

但整个想法有点违背类型安全枚举的概念。也许你可以使用老旧的不安全枚举?


这是如何“违反类型安全枚举的概念”的? - Silverspur
1
在这里进行强制转换,你会失去一些类型安全性。我的直觉告诉我,这可能不是你想要进行强制转换的最后一个地方。猜测一下,但是接下来的某一天或者在某个地方,你会想要检查其中一个OR标志是否被设置了,对吧?这时候AND就派上用场了。然后你想要将其传递给C API并实现转换。这样一点一点地,你会放弃它,并最终以混乱的"C+" hackery结束。但是你更了解你的项目和自己的纪律感,所以最终决定权在你手中。 - Ivan Aksamentov - Drop

1

如何将 Elem 枚举的值进行类型转换?

        first = int(Elem::A) | int(Elem::B),
        second = int(Elem::A) | int(Elem::C),

1
你需要对枚举类型进行静态转换,以便使用内置的operator |,转换成相应的整数类型。
class MyClass
{
    public: enum class Elem: unsigned int
    {
        A=1, B=2, C=4, D=8
    };
    public: enum class Group: unsigned int
    {
        first  = static_cast<unsigned int>(Elem::A) | static_cast<unsigned int>(Elem::B)
    ,   second = static_cast<unsigned int>(Elem::A) | static_cast<unsigned int>(Elem::C)
    };
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接