21

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.

RamblingMad
  • 5,332
  • 2
  • 24
  • 48
Narek
  • 38,779
  • 79
  • 233
  • 389
  • I think that's possible. But your code doesn't compile! – CinCout Sep 15 '15 at 06:01
  • Undefined behavior (after converting back and forth between `char`) if you try to test the `Flags` variable afterwards. I can try find it in the standard for you, but you should just do that yourself. – RamblingMad Sep 15 '15 at 06:02
  • 3
    @CoffeeandCode It's perfectly well-defined. – T.C. Sep 15 '15 at 06:03
  • 1
    Really? It doesn't compile for me. – Weak to Enuma Elish Sep 15 '15 at 06:05
  • @T.C. Pretty sure trying to test the value of an enum class variable with a value that isn't defined by the enum class is subject to undefined behavior (although not certain). If you test it against the underlying type I know it's defined for sure, though. – RamblingMad Sep 15 '15 at 06:10
  • If the answers given here are the best answers then my conclusion is that it isn't possible. If the automatic solution is more difficult than checking the validity of the values manually there is no point in it. – krowe Feb 26 '21 at 02:10

7 Answers7

29

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 :)

Narek
  • 38,779
  • 79
  • 233
  • 389
T.C.
  • 133,968
  • 17
  • 288
  • 421
  • `within the range of enumeration values` mean <=8 or <= 255 for the char type enum I have defined? <=255 is the correct answer, right? – Narek Sep 15 '15 at 06:17
  • @Narek The range of enumeration values is the same as the range of values of the underlying type, i.e., `std::numeric_limits::min()` to `std::numeric_limits::max()`. Usually, [-128, 127] or [0, 255], depending on your `char`'s signedness. – T.C. Sep 15 '15 at 06:18
  • 3
    You could always cast to [`std::underlying_type::type`](http://en.cppreference.com/w/cpp/types/underlying_type) then you don't need to specify an underlying type and it will still always be in range. – sjdowling Sep 15 '15 at 08:36
  • @sjdowling If the underlying type is not fixed, then it doesn't affect the range of enumeration values, and casting to it isn't enough. (I believe the rules there makes `|` and `&` still safe, but `~` may break.) RL example: [GCC bug 56158](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56158). – T.C. Sep 18 '15 at 17:24
  • And then how you can check if `Flags` contains a particular value? If I set `Flags flags = Flags::FLAG_1 | Flags::Flag_3` how can I know if `flags` contains `FLAG_2` for example? – Jepessen Jan 30 '23 at 09:46
15

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.

NutCracker
  • 11,485
  • 4
  • 44
  • 68
5

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));
}
Oleksa
  • 635
  • 5
  • 15
3

Please don't do this. If you need to do this, enum classs 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.

RamblingMad
  • 5,332
  • 2
  • 24
  • 48
  • Qt does this I guess with lots of flags to enable or disable a feature, right? – Narek Sep 15 '15 at 06:37
  • I would imagine they use an `enum` and just treat it as an int, that's useful to restrict values of a bit set. – RamblingMad Sep 15 '15 at 06:51
  • 2
    I don't think this has anything to do with the `enum` to be honest. You would get the same result if you just had straight variables for `one`, `two`, `three` and `four`. It seems to me the problem with this example is in the choice of the names that leads to an unexpected outcome rather than anything inherent with `enums`. – Galik Sep 15 '15 at 06:52
  • @Galik the names aren't supposed to be representations of the numbers 1, 2, 3, 4; but rather the first possible value, second possible value, etc. I'll change it to represent that better – RamblingMad Sep 15 '15 at 06:54
  • 3
    The error is in treating a *bitmask type* ([bitmask.types]) as an *enumerated type* (in the standard library sense, [enumerated.types]), rather than in the way chosen to implement a bitmask type. User code should treat bitmask types as opaque, and scoped enums help that by preventing implicit conversions. – T.C. Sep 15 '15 at 07:07
  • @T.C. which is why I said never directly using the value. Of course to make it an opaque bitmask you would define `operator|` as in your answer. – RamblingMad Sep 15 '15 at 07:11
1

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
    }
}
Questionable
  • 768
  • 5
  • 26
  • This is an interesting one, using namespaces was popular in times prior enum classes. I didn’t try it yet, but I assume it will not help regarding using enum values from a second enum definition by accident? I mean, we have not the same type safety as with enum classes, right? – Andi Aug 27 '21 at 09:07
  • As long as you don't create another enum in that same namespace it shouldn't have any issues with a second enum definition. The unique namespace is what allows the enum to work very similar to an enum class and the `using` statement at the bottom makes it so you don't need to remember the unique namespace so it acts just like an enum class that supports flags. – Questionable Oct 26 '21 at 20:33
0

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

CinCout
  • 9,486
  • 12
  • 49
  • 67
-1

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); 
nishantjr
  • 1,788
  • 1
  • 15
  • 39
  • 2
    Extremely overkill to just store some bits. An `std::bitset` would make much more sense than creating your own class. – RamblingMad Sep 15 '15 at 06:38