如何在作用域枚举上重载 |= 运算符?

问题描述 投票:0回答:7

如何在强类型(作用域)

|=
(在 C++11、GCC 中)上重载 
enum 运算符?

我想测试、设置和清除强类型枚举上的位。为什么强类型?因为我的书说这是很好的做法。但这意味着我必须到处

static_cast<int>
。为了防止这种情况,我重载了
|
&
运算符,但我不知道如何在枚举
上重载 
|= 运算符 。对于类,您只需将 运算符定义放入类中,但对于枚举,这在语法上似乎不起作用。

这是我到目前为止所拥有的:

enum class NumericType
{
    None                    = 0,

    PadWithZero             = 0x01,
    NegativeSign            = 0x02,
    PositiveSign            = 0x04,
    SpacePrefix             = 0x08
};

inline NumericType operator |(NumericType a, NumericType b)
{
    return static_cast<NumericType>(static_cast<int>(a) | static_cast<int>(b));
}

inline NumericType operator &(NumericType a, NumericType b)
{
    return static_cast<NumericType>(static_cast<int>(a) & static_cast<int>(b));
}

我这样做的原因是:这就是它在强类型 C# 中的工作方式:枚举只有一个带有其基础类型字段的结构体,以及在其上定义的一堆常量。但它可以具有适合枚举隐藏字段的任何整数值。

C++ 枚举似乎以完全相同的方式工作。在这两种语言中,都需要从 enum 到 int 进行强制转换,反之亦然。然而,在 C# 中,按位运算符默认是重载的,而在 C++ 中则不是。

c++ c++11 enums operator-overloading bitwise-operators
7个回答
50
投票
inline NumericType& operator |=(NumericType& a, NumericType b)
{
    return a= a |b;
}

这有效吗? 编译并运行:(Ideone)

#include <iostream>
using namespace std;

enum class NumericType
{
    None                    = 0,

    PadWithZero             = 0x01,
    NegativeSign            = 0x02,
    PositiveSign            = 0x04,
    SpacePrefix             = 0x08
};

inline NumericType operator |(NumericType a, NumericType b)
{
    return static_cast<NumericType>(static_cast<int>(a) | static_cast<int>(b));
}

inline NumericType operator &(NumericType a, NumericType b)
{
    return static_cast<NumericType>(static_cast<int>(a) & static_cast<int>(b));
}

inline NumericType& operator |=(NumericType& a, NumericType b)
{
    return a= a |b;
}

int main() {
    // your code goes here
    NumericType a=NumericType::PadWithZero;
    a|=NumericType::NegativeSign;
    cout << static_cast<int>(a) ;
    return 0;
}

打印3。


3
投票

这似乎对我有用:

NumericType operator |= (NumericType &a, NumericType b) {
    unsigned ai = static_cast<unsigned>(a);
    unsigned bi = static_cast<unsigned>(b);
    ai |= bi;
    return a = static_cast<NumericType>(ai);
}

但是,您仍然可以考虑为您的

enum
位集合定义一个类:

class NumericTypeFlags {
    unsigned flags_;
public:
    NumericTypeFlags () : flags_(0) {}
    NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {}
    //...define your "bitwise" test/set operations
};

然后,更改

|
&
运算符以返回
NumericTypeFlags


3
投票

读完这个问题后,我重载了对所有枚举有效的按位运算符。

template<typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
inline T operator~(const T& value)
{
    return static_cast<T>(~static_cast<int>(value));
}

template<typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
inline T operator|(const T& left, const T& right)
{
    return static_cast<T>(static_cast<int>(left) | static_cast<int>(right));
}

template<typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
inline T& operator|=(T& left, const T& right)
{
    return left = left | right;
}

template<typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
inline T operator&(const T& left, const T& right)
{
    return static_cast<T>(static_cast<int>(left) & static_cast<int>(right));
}

template<typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
inline T& operator&=(T& left, const T& right)
{
    return left = left & right;
}

template<typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
inline T operator^(const T& left, const T& right)
{
    return static_cast<T>(static_cast<int>(left) ^ static_cast<int>(right));
}

template<typename T, typename = std::enable_if_t<std::is_enum_v<T>>>
inline T& operator^=(T& left, const T& right)
{
    return left = left ^ right;
}

2
投票

我厌倦了所有带有枚举算术的样板,并转向更像这样的习语:

struct NumericType {
    typedef uint32_t type;
    enum : type {
        None                    = 0,

        PadWithZero             = 0x01,
        NegativeSign            = 0x02,
        PositiveSign            = 0x04,
        SpacePrefix             = 0x08
    };
};

这样我仍然可以传递

NumericType::type
参数以保持清晰,但我牺牲了类型安全性。

我考虑制作一个通用模板类来代替

uint32_t
,它将提供 one 算术重载的副本,但显然我不允许从类派生枚举,所以无论如何(感谢 C++!) .


0
投票

通过组合不同的值来创建新的、未定义的值,你完全违背了强类型范式。

看起来您正在设置完全独立的各个标志位。在这种情况下,将位组合成数据类型是没有意义的,因为这种组合会产生未定义的值。

您应该决定标志数据的大小(

char
short
long
long long
)并随之滚动。但是,您可以使用特定类型来测试、设置和清除标志:

typedef enum
{
    PadWithZero             = 0x01,
    NegativeSign            = 0x02,
    PositiveSign            = 0x04,
    SpacePrefix             = 0x08
} Flag;

typedef short Flags;

void SetFlag( Flags & flags, Flag f )
{
    flags |= static_cast<Flags>(f);
}

void ClearFlag( Flags & flags, Flag f )
{
    flags &= ~static_cast<Flags>(f);
}

bool TestFlag( const Flags flags, Flag f )
{
    return (flags & static_cast<Flags>)(f)) == static_cast<Flags>(f);
}

这是非常基本的,当每个标志只有一位时就很好了。对于屏蔽标志来说,情况要复杂一些。有多种方法可以将位标志封装到强类型类中,但这确实是值得的。就你的情况而言,我不相信是这样。


0
投票

为什么要强类型?因为我的书说这是很好的做法。

那么你的书就不会谈论你的用例。无作用域枚举适用于标志类型。

enum NumericType : int
{
    None                    = 0,

    PadWithZero             = 0x01,
    NegativeSign            = 0x02,
    PositiveSign            = 0x04,
    SpacePrefix             = 0x08
};

0
投票

我很惊讶你joas的回答效果这么好。基本上,它将 C++ 恢复为将

enum
视为
int

如果您没有为每个枚举定义按位操作,您可以使用宏为某些选择定义按位操作

enum class

#define BITWISE_OPS( ENUM )                  \
inline ENUM operator|( ENUM a, ENUM b ) {    \
  return (ENUM)( (int&)a | (int&)b );        \
}                                            \
inline ENUM operator&( ENUM a, ENUM b ) {    \
  return (ENUM)( (int&)a & (int&)b );        \
}                                            \
inline ENUM operator|=( ENUM& a, ENUM b ) {  \
  (int&)a |= (int&)b;                        \
  return a;                                  \
}                                            \
inline ENUM operator&=( ENUM& a, ENUM b ) {  \
  (int&)a &= (int&)b;                        \
  return a;                                  \
}                                            \

用途:

enum MouseButton {
  Left   = 1 << 0,
  Middle = 1 << 1,
  Right  = 1 << 2,
};

BITWISE_OPS( MouseButton )
© www.soinside.com 2019 - 2024. All rights reserved.