31

如何|=在强类型(作用域)enum(在 C++11、GCC 中)上重载运算符?

我想在强类型枚举上测试、设置和清除位。为什么要强类型?因为我的书说这是很好的做法。但这意味着我必须static_cast<int>无处不在。为了防止这种情况,我重载了|and&运算符,但我不知道如何在 enum 上|=重载运算符。对于一个类,您只需将运算符定义放在 class中,但对于在语法上似乎不起作用的枚举。

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

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++ 中则不是。

4

6 回答 6

41
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。

于 2013-04-08T21:39:45.480 回答
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

于 2013-04-08T22:06:42.693 回答
2

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

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

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

这样我仍然可以NumericType::type为了清楚起见传递参数,但我牺牲了类型安全。

我考虑制作一个通用模板类来代替uint32_t它提供算术重载的一个副本,但显然我不允许从一个类派生一个枚举,所以无论如何(感谢 C++!)。

于 2021-01-18T23:46:48.353 回答
1

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

那么你的书不是在谈论你的用例。无范围枚举适用于标志类型。

enum NumericType : int
{
    None                    = 0,

    PadWithZero             = 0x01,
    NegativeSign            = 0x02,
    PositiveSign            = 0x04,
    SpacePrefix             = 0x08
};
于 2020-03-23T13:22:59.063 回答
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);
}

这是非常基本的,当每个标志只有一个位时很好。对于屏蔽标志,它有点复杂。有一些方法可以将位标志封装到强类型类中,但它确实是值得的。在你的情况下,我不相信它是。

于 2013-04-08T21:57:04.217 回答
0

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

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;
}
于 2022-02-14T03:58:23.880 回答