How can I overload the |=
operator on a strongly typed (scoped) enum
(in C++11, GCC)?
I want to test, set and clear bits on strongly typed enums. Why strongly typed? Because my books say it is good practice. But this means I have to static_cast<int>
everywhere. To prevent this, I overload the |
and &
operators, but I can't figure out how to overload the |=
operator on an enum. For a class you'd simply put the operator definition in the class, but for enums that doesn't seem to work syntactically.
This is what I have so far:
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));
}
The reason I do this: this is the way it works in strongly-typed C#: an enum there is just a struct with a field of its underlying type, and a bunch of constants defined on it. But it can have any integer value that fits in the enum's hidden field.
And it seems that C++ enums work in the exact same way. In both languages casts are required to go from enum to int or vice versa. However, in C# the bitwise operators are overloaded by default, and in C++ they aren't.
inline NumericType& operator |=(NumericType& a, NumericType b)
{
return a= a |b;
}
This works? Compile and run: (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;
}
print 3.
This seems to work for me:
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);
}
However, you may still consider defining a class for your collection of enum
bits:
class NumericTypeFlags {
unsigned flags_;
public:
NumericTypeFlags () : flags_(0) {}
NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {}
//...define your "bitwise" test/set operations
};
Then, change your |
and &
operators to return NumericTypeFlags
instead.
By combining distinct values to make new, undefined values, you are totally contradicting the strong-typing paradigm.
It looks like you are setting individual flag bits that are completely independent. In this case, it does not make sense to combine your bits into a datatype where such a combination yields an undefined value.
You should decide on the size of your flag data (char
, short
, long
, long long
) and roll with it. You can, however, use specific types to test, set and clear flags:
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);
}
This is very basic, and is fine when each flag is only a single bit. For masked flags, it's a bit more complex. There are ways to encapsulate bit flags into a strongly-typed class, but it really has to be worth it. In your case, I'm not convinced that it is.
Why strongly typed? Because my books say it is good practice.
Then your books are not talking about your use case. Unscoped enumerations are fine for flag types.
enum NumericType : int
{
None = 0,
PadWithZero = 0x01,
NegativeSign = 0x02,
PositiveSign = 0x04,
SpacePrefix = 0x08
};