如何使枚举类与“按位或”功能一起工作?

10

我通常使用带有“按位或”或|enum来允许一个对象拥有一些选项。如何使枚举类与“按位或”功能配合工作?


没有什么区别,你可能需要将其转换为int类型。 - aaronman
这个问题的一些答案可能会让你感兴趣:如何在C语言中设置、清除和切换单个位? - DavidRR
1个回答

16
你需要为你的枚举类重载运算符,并通过将其转换为底层类型来实现它们:
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 { };

2
C++ 没有提供这个功能是一个疏忽,我认为。枚举不是位域,它们是枚举。 - Lightness Races in Orbit
4
您似乎在声称在C++中使用枚举来表示位标志不是一种被广泛使用的、既定的模式。顺便说一下,C#甚至提供了显式支持将某些枚举视为位标志。 - Konrad Rudolph
3
@Lightness 但为什么不能呢?这是一个非常实用的成语,我不认为存在问题。最多你可以说它混淆了接口和实现,但那是过度工程化,而且可以通过提供方便函数来轻松地缓解,正如我回答中的代码注释所概述的那样。 - Konrad Rudolph
3
请检查关于枚举范围的措辞。它明确扩展了范围,以便OR(按位或)在一起的枚举值也在范围内。这不是巧合,CWG明确有意如此。(如果我没记错的话,是在2004年10月,雷德蒙德)。我希望有一个 enum class Foo { operator| = default; } 的语法。 - MSalters
2
让我用更强烈的措辞:考虑到为了使 operator| 给出一个在范围内的值而做的所有工作,这个操作符竟然还缺失,真是令人惊讶。 - MSalters
显示剩余8条评论

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