How to Use C++11 Enum Class for Flags

How to use C++11 enum class for flags

You need to write your own overloaded operator| (and presumably operator& etc.).

Flags operator|(Flags lhs, Flags rhs) 
{
return static_cast<Flags>(static_cast<char>(lhs) | static_cast<char>(rhs));
}

Conversion of an integer to an enumeration type (scoped or not) is well-defined as long as the value is within the range of enumeration values (and UB otherwise; [expr.static.cast]/p10). For enums with fixed underlying types (this includes all scoped enums; [dcl.enum]/p5), the range of enumeration values is the same as the range of values of the underlying type ([dcl.enum]/p8). The rules are trickier if the underlying type is not fixed - so don't do it :)

How does one use an enum class as a set of flags?

It is certainly possible to use enum classes for bitmaps. It is, unfortunately, a bit painful to do so: You need to define the necessary bit operations on your type. Below is an example how this could look like. It would be nice if the enum classes could derive from some other type which could live in a suitable namespace defining the necessary operator boilerplate code.

#include <iostream>
#include <type_traits>

enum class bitmap: unsigned char
{
a = 0x01,
b = 0x02,
c = 0x04
};

bitmap operator& (bitmap x, bitmap y)
{
typedef std::underlying_type<bitmap>::type uchar;
return bitmap(uchar(x) & uchar(y));
}

bitmap operator| (bitmap x, bitmap y)
{
typedef std::underlying_type<bitmap>::type uchar;
return bitmap(uchar(x) | uchar(y));
}

bitmap operator^ (bitmap x, bitmap y)
{
typedef std::underlying_type<bitmap>::type uchar;
return bitmap(uchar(x) ^ uchar(y));
}

bool test(bitmap x)
{
return std::underlying_type<bitmap>::type(x);
}

int main()
{
bitmap v = bitmap::a | bitmap::b;
if (test(v & bitmap::a)) {
std::cout << "a ";
}
if (test(v & bitmap::b)) {
std::cout << "b ";
}
if (test(v & bitmap::c)) {
std::cout << "c ";
}
std::cout << '\n';
}

How to use enums as flags in C++?

The "correct" way is to define bit operators for the enum, as:

enum AnimalFlags
{
HasClaws = 1,
CanFly = 2,
EatsFish = 4,
Endangered = 8
};

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

Etc. rest of the bit operators. Modify as needed if the enum range exceeds int range.

c++: OR operator: enum vs enum class?

enum eDogType values are processed as int values, where enum class eDogType values are not (they are processed as values of type eDogType).

So in the first case, the operator | (int, int) is used, whereas in the second case, the operator | (eDogType, eDogType) is needed (but not found).



#include <iostream>

enum class eDogType
{
eHusk = 0,
eGold,
eAus,
eGerm,
ePud
};

int main()
{
eDogType eDog1 = eDogType::eAus;
eDogType eDog2 = eDogType::eGerm;

eDogType eDog = static_cast<eDogType>(static_cast<int>(eDog1) | static_cast<int>(eDog2));

std::cout << "Hello World: " << static_cast<int>(eDog) << "\n";

return(0);
}

C++ enum flags vs bitset

Do you compile with optimization on? It is very unlikely that there is a 24x speed factor.

To me, bitset is superior, because it manages space for you:

  • can be extended as much as wanted. If you have a lot of flags, you may run out of space in the int/long long version.
  • may take less space, if you only use just several flags (it can fit in an unsigned char/unsigned short - I'm not sure that implementations apply this optimization, though)

Bitwise operations in Enumarators

Is the approach correct and how can we remove the compilation error?

The approach is subject to problems. If you work with your variables carefully, you can get by with using the enum.

The line

perms &= ~Permissions::Executable;

is equivalent to

perms = perms & ~Permissions::Executable;

The cause of the compiler error is that the bitwise operators result in an int and you are trying to assign an int to an enum without a cast.

You'll have to use a cast just like you did with the first operation to get rid of the compiler error.

perms = static_cast<Permissions>(perms & ~Permissions::Executable);

Update, in response to OP's comment

The value Readable | Writable is 0x6, which does not correspond to the value of any of the tokens in the enum.
Hence, if you use:

Permissions p1 = Permissions::Readable;
Permissions p2 = Permissions::Writable;

Permissions p3 = static_cast<Permissions>(p1 | p2);

you will have a case where the value of p3 does not match any of the known tokens. If you have if/else blocks or switch statements that expect the values of all variables of type Permissions to be one of the known tokens, you will notice unexpected behavior in your code.



Related Topics



Leave a reply



Submit