Search code examples
c++enumsbitmask

How to set enum val of fixed size to its max possible value?


This is probably simple but I'm not getting it right. I have a "bitmask" enum which has a value all that indicates that all bits are set. However, I can't get it to flip all bits using ~0. The following error appears:

<source>:11:16: error: enumerator value '-1' is outside the range of underlying type 'uint_fast8_t' {aka 'unsigned char'}
   11 |         all = ~0x0,
      |             

Which is strange because it should actually fit into uint8_t no? Here is my code (godbolt):

#include <iostream>

int main()
{
    enum mask_t : uint_fast8_t {
        first = 0x1,
        second = 0x2,
        all = ~0x0,
    } mask;

    mask = all;
}

Solution

  • By default, 0x0 is of type int. So if you try to flip all the bits you'll get -1 which can't be assigned to the type your enumeration was defined to.

    Even if you use a suffix for that literal value, like u for example. To indicate that the literal value is of unsigned type. As in ~0x0u. You'd get the maximum of the unsigned int type. Which exceeds the range of the 8-bit integer type you're using. So this doesn't work either.

    So you need to tell the language that you want the literal value to be of the type you need first. That can be achieved with a static_cast as demonstrated in other answers:

    static_cast<uint_fast8_t>( ~0x0 )
    

    But using hardcoded types and values can get in the way sometimes if you decide to change the type of the enum later. So if you have c++14 available. You can use the std::underlying_type type-trait and make a generic utility like:

    template < class T > constexpr std::underlying_type_t< T > enum_max_v = ~static_cast< std::underlying_type_t< T > >(0);
    // If you have c++17 available with the inline keyword
    // template < class T > inline constexpr std::underlying_type_t< T > enum_max_v = ~static_cast< std::underlying_type_t< T > >(0);
    

    And then used like:

    enum mask_t : uint_fast8_t {
        first = 0x1,
        second = 0x2,
        all = enum_max_v< mask_t >,
    } mask;
    

    Now you don't have to care about the underlying type of the enumeration.

    You can even use std::numeric_limits if you want the right values instead of relying on flipping bits:

    template < class T > constexpr std::underlying_type_t< T > enum_max_v = std::numeric_limits< std::underlying_type_t< T > >::max();
    

    Sky is the limit.