29 Jun 2024 • C++ tricks: enum arithmetic

It's annoying having to write stuff like

for( MyEnum i = MyEnum( 0 ); i < MyEnum_Count; i = MyEnum( i + 1 ) ) {

Doubly so with enums that are really bitfields where you can actually do arithmetic, they just return non-enum types and C++ doesn't allow the implicit casts back to the enum in all situations.

At some point you might get sick of this and start implementing arithmetic operators on your enums, but why not just do it for all of them?

template< typename E > concept IsEnum = __is_enum( E );
template< typename E > using UnderlyingType = __underlying_type( E );

template< IsEnum E > void operator++( E & x, int ) { x = E( UnderlyingType< E >( x ) + 1 ); }
template< IsEnum E > void operator&=( E & lhs, E rhs ) { lhs = E( UnderlyingType< E >( lhs ) & UnderlyingType< E >( rhs ) ); }
template< IsEnum E > void operator|=( E & lhs, E rhs ) { lhs = E( UnderlyingType< E >( lhs ) | UnderlyingType< E >( rhs ) ); }

// you can do these in base C++ but they return ints and MyEnum x = int; doesn't compile
template< IsEnum E > constexpr E operator&( E lhs, E rhs ) { return E( UnderlyingType< E >( lhs ) & UnderlyingType< E >( rhs ) ); }
template< IsEnum E > constexpr E operator|( E lhs, E rhs ) { return E( UnderlyingType< E >( lhs ) | UnderlyingType< E >( rhs ) ); }
template< IsEnum E > constexpr E operator~( E x ) { return E( ~UnderlyingType< E >( x ) ); }

Every compiler supports the same intrinsics, so no need for compiler specific code or the STL.