49

I have some generic code that works with flags specified using C++11 enum class types. At one step, I'd like to know if any of the bits in the flag are set. Currently, I'm using the code:

if (flags != static_cast<E>(0)) // Works, but ugly.

I could also force users to specify a particular name for an all-zero field, which is more readable but imposes my naming conventions on anyone using it:

if (flags != E::none) // Works, if you manually define none = 0.

But neither of these reads as nicely as the traditional:

if (flags) // Doesn't work with class enums.

Is it possible to specify a custom function to evaluate a class enum in a boolean context?

  • Is there a specific reason you can't use binary flags? They're clean and efficient. Anyhow, enum class values cannot be converted to numbers. You have to use flags != E::none if you're enumerating. You know, you can just make a non-enum class and using static const int whatever = 1, static const int whatever_else = 2...etc. and overload just a few operators. – std''OrgnlDave Mar 26 '12 at 16:14
  • 1
    @OrgnlDave: "Binary flags" meaning what, bools? As bitfields they're often IB/UB and with regular alignment I'd not call them efficient, nor clean if I need to copy multiple ones at a time. I don't see the value in suggesting static ints, I could just use a regular enum and get at least a little type-safety. –  Mar 26 '12 at 16:41
  • "I could just use a regular enum and get at least a little type-safety." Then you have to accept that along with that you no longer get to speak about bits. – GManNickG Mar 26 '12 at 18:13
  • 1
    @GManNickG: No I don't; one of the fundamental points of enum classes is that they are typesafe ways of naming a particular (set of) value representation(s). It is absolutely fine to speak both of bits and type-safety. –  Mar 26 '12 at 20:29
  • @JoeWreschnig: You may talk about bits of particular named constants, sure, but not about the enum value in general. This is by design and a Good Thing. – GManNickG Mar 26 '12 at 20:33
  • 3
    @GManNickG: Enum classes remove the implicit cast to int but _do not hide or change the value representation_. You still specify the value literally, you can still static_cast rather than reinterpret_cast, and you can still use std::underlying_type. –  Mar 26 '12 at 20:40
  • @JoeWreschnig: Indeed we agree on all of this. But those things exist merely to go from the enum to the underlying type and back, not to go from a specific general value of the underlying type to the enum. – GManNickG Mar 26 '12 at 21:09
  • [std::to_underlying](https://en.cppreference.com/w/cpp/utility/to_underlying) got accepted in C++23, so this will be another option soon. – Vinci May 15 '21 at 14:31
  • `if (flags != E{})` also works. `auto operator+(E e) { return static_cast::type>(e); }` then `if (+flags)` will work. (C++23 `std::to_underlying` for even more legible syntax, as per Vinci.) Or provided `operator!`, then `if (!!flags)` would work, albeit idiomatic JavaScript style, which is uncommon C++ style (because it typically isn't needed since zero is *falsy* and non-zero is *truthy* in C++). – Eljay Jul 03 '22 at 15:14

8 Answers8

39

Like @RMatin says. But you could overload operator!

bool operator!(E e) {
  return e == static_cast<E>(0);
}

So that you can use the !!e idiom

if(!!e) {
  ...
}
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
24

Is it possible to specify a custom function to evaluate a class enum in a boolean context?

Yes, but not automatically. Manually calling a function is still more elegant than the other alternatives presented.

Simply pick a nice function name, such as any, and implement it. Overload resolution will make sure your function plays well with all others.

bool any( E arg )
    { return arg != E::none; }

...

if ( any( flags ) ) {
    ...

Looks nice enough to me.


Update: if you want this to apply to several enumeration types, it can be templated:

template< typename enum_type > // Declare traits type
struct enum_traits {}; // Don't need to declare all possible traits

template<>
struct enum_traits< E > { // Specify traits for "E"
    static constexpr bool has_any = true; // Only need to specify true traits
};

template< typename enum_type > // SFINAE makes function contingent on trait
typename std::enable_if< enum_traits< enum_type >::has_any,
    bool >::type
any( enum_type e )
    { return e != enum_type::none; }

I've been using this sort of mechanism for other things and never encountered any side effects or issues :v) .

You could skip the trait and set the SFINAE condition to something like enum_type::none == enum_type::none, to merely check for the presence of none and the equality operator, but that would be less explicit and safe.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
13
struct Error {
    enum {
        None        = 0,
        Error1      = 1,
        Error2      = 2,
    } Value;

    /* implicit */ Error(decltype(Value) value) : Value(value) {}

    explicit operator bool() {
        return Value != Error::None;
    }
};

inline bool operator==(Error a, Error b) {
    return a.Value == b.Value;
}

inline bool operator!=(Error a, Error b) {
    return !(a == b);
}

enum has no overloaded operator for now, so wrap it in a class or struct.

kezhuw
  • 141
  • 1
  • 5
11

If you have a flags field (ie: a bitfield) I would strongly advise you not to use enum class for bitfields.

Strongly typed enums exist to be, well, strongly typed. It makes the enumerators into something more than just named constant integers the way regular enums are. The idea is that, if you have a variable of an enum class type, then its contents should always exactly match one of the enumerator values. That's why there is no implicit conversion from or to integer types.

But that's not what you're doing. You're taking a bitfield, which is a composition of enumerator values. That composition is not itself any one of those values; it's a combination of them. Therefore, you're lying when you say that you're taking the enum class type; you're really just taking an unsigned integer that might be one of the enum class enumerators.

For example:

enum class Foo
{
  First   = 0x01,
  Second  = 0x02,
  Third   = 0x04,
};

Foo val = Foo::First | Foo::Second;

val in this case does not contain First, Second, or Third. You've lost strong typing, because it doesn't contain any of the types.

enum class values cannot be implicitly converted to bool; they cannot be implicitly converted to integers; and they cannot implicitly have most math operations performed on them. They are opaque values.

And thus they are inappropriate for use as bitfields. Attempting to use enum class in such an inappropriate way will only lead to a lot of casting. Just use a regular old enum and save yourself the pain.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 13
    "You've lost strong typing, because it doesn't contain any of the types." You mean, it doesn't contain any of the predefined _values_. That doesn't mean I've made any particular kind of _type_ violation. Furthermore, they are _not_ opaque values - their value representation is defined in terms of the underlying storage type - they are merely _explicit_ values. I appreciate the suggestion that enum classes aren't the right tool here, but saying what I want is "illegal" rather than "needs to be explicit to the point where it's so verbose it's no longer valuable" is inaccurate. –  Mar 27 '12 at 02:04
  • 1
    @JoeWreschnig: First, I said that the code I wrote was illegal, which it *is*. Second, what you want *requires* a lot of explicit and pointless casts, which is the problem you asked us to solve. The solution is to stop using things that require explicit and pointless casts. Third, it is a *semantic* type violation. The whole point of having strong-typing for enums is to have *some* syntactic assurance that the values being passed to you are one of the legal values. Yes, people can lie, but they at least have to cast to do that, which is often considered a sign of doing something shady. – Nicol Bolas Mar 27 '12 at 02:09
  • 2
    -1: Funny, I just answered another question about how `enum`s are ideal for sets of flags. The problem with the ideone example is that the `operator|` overload is missing. `enum` types are defined to follow the value semantics of the underlying type, and if that supports bitwise arithmetic, then type safety and arithmetic safety are both optimized. – Potatoswatter Mar 27 '12 at 04:27
  • @Potatoswatter: `enum`s are, yes. `enum class`es are *not*. `enum class` values do not follow value semantics. They're **strongly typed**; they're only for storing one of the enumeration values. So you can't just do any math on them. Note that the Ideaone code works if you change it to `enum` rather than `enum class`. It's also illegal to store numbers in an `enum class` variable without an explicit cast. So even if `operator|` worked, you'd still need a cast just to store the resulting value. – Nicol Bolas Mar 27 '12 at 05:03
  • @Nicol: "It's also illegal to store numbers in an enum class variable without an explicit cast. So even if operator| worked, you'd still need a cast just to store the resulting value." — yep, that's exactly what you would do. If the default semantics aren't enough, you add customization. http://ideone.com/yLBZF – Potatoswatter Mar 27 '12 at 05:17
  • @Potatoswatter: Then what's the point of using an `enum class` instead of an `enum`? If you're just going to cast away all the strong typing, there's really no point in using it. – Nicol Bolas Mar 27 '12 at 05:18
  • Remember that these operators aren't predefined at all for enumeration types. Being able to do `e1 | e2` for old-style enumerators relies on converting them to `int` first. There's no relation to "value semantics" of an enum type there. – Potatoswatter Mar 27 '12 at 05:20
  • @Nicol: Because the casts are safely hidden inside a function. As an implementation detail, the implementor can use them where they're safe to define only safe operations. `enum class` is just generally safer than `enum`. It would take a lot of work to really eliminate all the extra safety. – Potatoswatter Mar 27 '12 at 05:22
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/9345/discussion-between-nicol-bolas-and-potatoswatter) – Nicol Bolas Mar 27 '12 at 05:25
  • I just want to point out that Microsoft Visual Studio will actually properly show flag-based class enums properly in IntelliSense tooltips and other contexts, correctly when multiple flags are specified. – Mike Weir Apr 19 '17 at 12:46
  • I don't think this is right. E.g. see the `enum class Handle` example [here](https://en.cppreference.com/w/cpp/language/enum). That page makes no mention of the expectation that `enum class`es can only take on the explicitly listed values, even though you might reasonably expect that (I did too before I looked it up). – Timmmm Oct 12 '21 at 10:01
  • 1
    I think it's too bad there was oversight in designing `enum class` with respect to using an enum as a bitfield. I suppose it's only a matter of time before a completely new and distinct type `enum flags` is defined ... – bobobobo Mar 16 '22 at 00:31
8

No, not like that. Conversion operators must be members, and enums cannot have members. I think the best you can do is comparison with none, or, if there isn't a none enumerator, wrap the static_cast in a function.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
0
if (flags != E{})

is less ugly than

if (flags != static_cast<E>(0))
Jens
  • 187
  • 8
-1

A short example of enum-flags below.

#indlude "enum_flags.h"

ENUM_FLAGS(foo_t)
enum class foo_t
    {
     none           = 0x00
    ,a              = 0x01
    ,b              = 0x02
    };

ENUM_FLAGS(foo2_t)
enum class foo2_t
    {
     none           = 0x00
    ,d              = 0x01
    ,e              = 0x02
    };  

int _tmain(int argc, _TCHAR* argv[])
    {
    if(flags(foo_t::a & foo_t::b)) {};
    // if(flags(foo2_t::d & foo_t::b)) {};  // Type safety test - won't compile if uncomment
    };

ENUM_FLAGS(T) is a macro, defined in enum_flags.h (less then 100 lines, free to use with no restrictions).

Yuri Yaryshev
  • 991
  • 6
  • 24
-1

I usually overload the unary + operator for flag-like enum classes, so that i can do the following:

#define ENUM_FLAGS (FlagType, UnderlyingType)                           \
    /* ... */                                                           \
    UnderlyingType operator+(const FlagType &flags) {                   \
          return static_cast<UnderlyingType>(flags)                     \
    }                                                                   \
    /* ... */                                                           \
    FlagType operator&(const FlagType &lhs, const FlagType &rhs) {      \
          return static_cast<FlagType>(+lhs & +rhs)                     \
    }                                                                   \
    /* ... */                                                           \
    FlagType &operator|=(FlagType &lhs, const FlagType &rhs) {          \
          return lhs = static_cast<FlagType>(+lhs | +rhs)               \
    }                                                                   \
    /* ... */                                                           \
    /***/

// ....

enum class Flags: std::uint16_t {
    NoFlag  = 0x0000,
    OneFlag = 0x0001,
    TwoFlag = 0x0002,
    // ....      
    LastFlag = 0x8000
};

ENUM_FLAGS(Flags, std::uint16_t)

auto flagVar = Flags::NoFlag;

// ...

flagVar |= Flags::OneFlag;

// ...

if (+(flagVar & Flags::OneFlag)) {
    /// ...
}