Reputation: 39871
Say I have such a class:
enum class Flags : char
{
FLAG_1 = 1;
FLAG_2 = 2;
FLAG_3 = 4;
FLAG_4 = 8;
};
Now can I have a variable that has type flags and assign a value 7
for example? Can I do this:
Flags f = Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3;
or
Flags f = 7;
This question arises because in the enum I have not defined value for 7
.
Upvotes: 23
Views: 26718
Reputation: 67244
Use enum class
only for enums that are distinct states and cannot be bitwise combined
Don't use enum class
for enums that you intend to use as bitwise combineable enums
Operator overloading your enum class
is really code bloat is not sensible to be doing when enum
provides that by default
If you're experiencing name collisions, you can use very long names for your enum:
enum ColorComponent {
ColorComponentNone = 0,
ColorComponentRed = 1 << 0,
ColorComponentGreen = 1 << 1,
ColorComponentBlue = 1 << 2,
};
Use like:
ColorComponent cc = (ColorComponent)(ColorComponentRed | ColorComponentBlue);
Another way to avoid name collisions is to use this classic trick of namespace
ing the enum:
namespace ColorComponent {
enum Enum {
None = 0,
Red = 1 << 0,
Green = 1 << 1,
Blue = 1 << 2,
};
};
You can use that enum
like:
ColorComponent::Enum e = (ColorComponent::Enum)(ColorComponent::Red | ColorComponent::Blue);
Consider also safety issues in Why is enum class considered safer to use than plain enum?
If you can handle the idea that your enum
implicitly converts to int
, you shouldn't use enum class
for bitwise combinable enum
s
If you completely insist on using enum class
because your brain is locked in a gear that says "MUST USE C++ NEW enum class
FEATURE", then you should probably save yourself a lot of code repeat and use a macro to operator overload your enum class
that want bitwise ops defined for them
Upvotes: 0
Reputation: 878
I realize this question is a bit old, but I will write out a method I have used to do this.
(If anything, if I Google this again in the future I have it documented to find again.)
Personally I like this method because intellisense (at least the VSCode version of it... I don't have Visual Studio on Linux...) will automatically pick up on what you're doing and give you useful hints. Also, it avoids the use of macros, so the compiler can warn you if it is not happy. Lastly, without the comments, it isn't a lot of code. You're not making a class and setting a bunch of overloads or anything, but you're still getting the benefit of scoped enums so that you can reuse a flag name/value for another enum. Anyway onto the example.
namespace FlagsNS
{
/* This is an old/classic style enum so put it in a
namespace so that the names don't clash
(ie: you can define another enum with the values of
Flag_1 or Flag_2, etc... without it blowing up)
*/
enum Flags
{
Flag_1 = 1 << 0, //Same as 1
Flag_2 = 1 << 1, //Same as 2
Flag_3 = 1 << 2, //Same as 4
Flag_4 = 1 << 3 //Same as 8
};
}
/* This is telling the compiler you want a new "type" called Flags
but it is actually FlagsNS::Flags. This is sort of like using the
#define macro, except it doesn't use the preprocessor so the
compiler can give you warnings and errors.
*/
using Flags = FlagsNS::Flags;
//Later in code.... so int main() for example
int main()
{
//If you don't mind c-style casting
Flags flag = (Flags)(Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3);
//Or if you want to use c++ style casting
Flags flag = static_cast<Flags>(Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3);
//Check to see if flag has the FLAG_1 flag set.
if (flag & Flags::FLAG_1)
{
//This code works
}
}
Upvotes: 3
Reputation: 675
It should handle any enumeration type. I'm not sure it doesn't have any side effects and is completely valid C++ code. Let me know if there are some issues.
template<class T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
constexpr T operator|(T lhs, T rhs)
{
return static_cast<T>(
static_cast<std::underlying_type<T>::type>(lhs) |
static_cast<std::underlying_type<T>::type>(rhs));
}
Upvotes: 5
Reputation: 12263
It's maybe better to make use of std::underlying_type
instead of hard-coding char
type.
Flags operator|(Flags lhs, Flags rhs) {
return static_cast<Flags>(
static_cast<std::underlying_type<Flags>::type>(lhs) |
static_cast<std::underlying_type<Flags>::type>(rhs)
);
}
Now, you can change the underlying type of your enumeration without needing to update that type in every bitwise operator overload.
Upvotes: 18
Reputation: 5518
Please don't do this. If you need to do this, enum class
s probably aren't what you need.
@T.C. showed you how to do it so long as you specify underlying type, but you will run into places where your program does things it just shouldn't.
An example is where you use a switch
and have a case
for every defined enum value.
e.g.
enum class my_enum: unsigned int{
first = 1,
second = 2,
third = 4,
fourth = 8
};
int main(){
auto e = static_cast<my_enum>(static_cast<unsigned int>(my_enum::first) | static_cast<unsigned int>(my_enum::second));
switch(e){
case my_enum::first:
case my_enum::second:
case my_enum::third:
case my_enum::fourth:
return 0;
}
std::cout << "Oh, no! You reached a part of the program you weren't meant to!\n";
return 1;
}
Will output:
Oh, no! You reached a part of the program you weren't meant to!
then return the error code 1
.
Which is also an example of why you should always have a default
case, of course, but that isn't my point.
Of course, you could argue that so long as the user of the enum class
never directly uses the value other than passing to a function; it would be a good way of restricting the values of a bitset. But I've always been a little too trustworthy and find std::uint[n]_t
and some constexpr
variables the best way (if a user sets an invalid bit it simply does nothing).
What you're doing just isn't really suitable for enum class
, because it defeats the purpose of having a scoped enumeration. You can no longer enumerate the values if you set it to an undefined one.
Upvotes: 2
Reputation: 137315
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 :)
Upvotes: 30
Reputation: 1820
At this point, It probably makes sense to define your own class to handle this.
/** Warning: Untested code **/
struct Flag {
static Flag Flag_1;
static Flag Flag_2;
static Flag Flag_3;
static Flag Flag_4;
Flag operator = (Flag);
private:
char const value;
};
Flag operator | (Flag, Flag);
Upvotes: -1
Reputation: 9619
The code in question doesn't compile. But you can do something like this,
enum class Flags : char
{
FLAG_1 = 1,
FLAG_2 = 2,
FLAG_3 = 4,
FLAG_4 = 8,
};
int main() {
Flags f = static_cast<Flags>(7);
Flags f1 = static_cast<Flags>( static_cast<char>(Flags::FLAG_1) | static_cast<char>(Flags::FLAG_2) | static_cast<char>(Flags::FLAG_3) );
return 0;
}
and it works
Upvotes: 0