【问题标题】:How to make enum class to work with the 'bit-or' feature?如何使枚举类与“位或”功能一起使用?
【发布时间】:2026-02-11 05:35:01
【问题描述】:

我通常将enum 与“位或”或| 一起使用,以允许对象具有一些选项。如何使枚举类与“位或”功能一起使用?

【问题讨论】:

标签: c++ enum-class


【解决方案1】:

您需要重载枚举类的运算符并通过转换为基础类型来实现它们:

enum class foo : unsigned {
    bar = 1,
    baz = 2
};

foo operator |(foo a, foo b) {
    return static_cast<foo>(static_cast<unsigned>(a) | static_cast<unsigned>(b));
}

...当然这可以概括(使用 SFINAE 和std::underlying_type)。在我看来,C++ 没有提供开箱即用的功能是一种疏忽。

一般实现如下所示:

// Intentionally undefined for non-enum types.
template <typename T, bool = std::is_enum<T>::value>
struct is_flag;

template <typename T>
struct is_flag<T, true> : std::false_type { };

template <typename T, typename std::enable_if<is_flag<T>::value>::type* = nullptr>
T operator |(T lhs, T rhs) {
    using u_t = typename std::underlying_type<T>::type;
    return static_cast<T>(static_cast<u_t>(lhs) | static_cast<u_t>(rhs));
}

// … same for `&`, `~`. And maybe functions like `isset`, `set` and `unset`.

此实现确保仅在实际充当标志的枚举中找到重载。要将枚举标记为标志,您需要专门化is_flag

enum class a_flag : unsigned {
    foo = 0,
    bar = 1,
    baz = 2
};

template <> struct is_flag<a_flag> : std::true_type { };

【讨论】:

  • That C++ doesn’t provide this out of the box is an oversight, in my opinion. 枚举不是位域。它们是枚举。
  • @LightnessRacesinOrbit 您似乎声称将枚举用于位标志并不是 C++ 中广泛使用的既定模式。顺便说一句,C# 更进一步,提供了显式支持,将某些枚举视为位标志。
  • 我从未声称它不是一种广泛使用或建立的模式。我的观点是不应该这样,因为这不是 C++ 中的枚举。对它们进行 ORing 不是 C++ 中内置的,只会重新强化这一点。我很高兴 C# 有自己版本的具有不同语义的这种语言特性,但 C# 不是 C++。
  • @Lightness 但是为什么不应该这样?这是一个非常实用的成语,我看不出有什么问题。充其量你可以说它是混合接口和实现,但这纯粹是过度设计,也可以通过提供便利功能来轻松缓解,如我回答中的代码注释中所述。
  • @LightnessRacesinOrbit:检查有关枚举范围的措辞。它显式地扩展了范围,以便枚举值 OR-ed 一起也在范围内。绝非巧合,CWG 明确表示要这样做。 (2004 年 10 月,雷德蒙德,IIRC)。我想要enum class Foo { operator| = default; } 语法。