C++ – Using scoped enums for bit flags in C++

bitwise-operatorscc++11enum

An enum X : int (C#) or enum class X : int (C++11) is a type that has a hidden inner field of int that can hold any value. In addition, a number of predefined constants of X are defined on the enum. It is possible to cast the enum to its integer value and vice versa. This is all true in both C# and C++11.

In C# enums are not only used to hold individual values, but also to hold bitwise combinations of flags, as per Microsoft's recommendation. Such enums are (usually, but not necessarily) decorated with the [Flags] attribute. To make the lives of developers easier, the bitwise operators (OR, AND, etc…) are overloaded so that you can easily do something like this (C#):

void M(NumericType flags);

M(NumericType.Sign | NumericType.ZeroPadding);

I am an experienced C# developer, but have been programming C++ only for a couple of days now, and I am not known with the C++ conventions. I intend to use a C++11 enum in the exact same way as I was used to do in C#. In C++11 the bitwise operators on scoped enums are not overloaded, so I wanted to overload them.

This solicited a debate, and opinions seem to vary between three options:

  1. A variable of the enum type is used to hold the bit field, similar to C#:

    void M(NumericType flags);
    
    // With operator overloading:
    M(NumericType::Sign | NumericType::ZeroPadding);
    
    // Without operator overloading:
    M(static_cast<NumericType>(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding)));
    

    But this would counter the strongly typed enum philosophy of C++11's scoped enums.

  2. Use a plain integer if you want to store a bitwise combination of enums:

    void M(int flags);
    
    M(static_cast<int>(NumericType::Sign) | static_cast<int>(NumericType::ZeroPadding));
    

    But this would reduce everything to an int, leaving you with no clue as to which type you're supposed to put in the method.

  3. Write a separate class that will overload operators and hold the bitwise flags in a hidden integer field:

    class NumericTypeFlags {
        unsigned flags_;
    public:
        NumericTypeFlags () : flags_(0) {}
        NumericTypeFlags (NumericType t) : flags_(static_cast<unsigned>(t)) {}
        //...define BITWISE test/set operations
    };
    
    void M(NumericTypeFlags flags);
    
    M(NumericType::Sign | NumericType::ZeroPadding);
    

    (full code by user315052)

    But then you have no IntelliSense or whatever support to hint you at the possible values.

I know this is a subjective question, but: What approach should I use? What approach, if any, is the most widely recognized in C++? What approach do you use when dealing with bit fields and why?

Of course since all three approaches work, I'm looking for factual and technical reasons, generally accepted conventions, and not simply personal preference.

For example, because of my C# background I tend to go with approach 1 in C++. This has the added benefit that my development environment can hint me on the possible values, and with overloaded enum operators this is easy to write and understand, and quite clean. And the method signature shows clearly what kind of value it expects. But most people here disagree with me, probably for good reason.

Best Answer

The simplest way is to provide the operator overloads yourself. I am thinking of creating a macro to expand the basic overloads per type.

#include <type_traits>

enum class SBJFrameDrag
{
    None = 0x00,
    Top = 0x01,
    Left = 0x02,
    Bottom = 0x04,
    Right = 0x08,
};

inline SBJFrameDrag operator | (SBJFrameDrag lhs, SBJFrameDrag rhs)
{
    using T = std::underlying_type_t <SBJFrameDrag>;
    return static_cast<SBJFrameDrag>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
    
inline SBJFrameDrag& operator |= (SBJFrameDrag& lhs, SBJFrameDrag rhs)
{
    lhs = lhs | rhs;
    return lhs;
}

(Note that type_traits is a C++11 header and std::underlying_type_t is a C++14 feature.)