Type-safe bitwise operations - TL;DR
How to use Boost.Flags
-
include the header
<boost/flags.hpp>
-
opt-in for an enumeration
E
by defining
BOOST_FLAGS(E)
in the same namespace as E
.
Example on Godbolt compiler explorer
#include <boost/flags.hpp>
enum class pizza_toppings {
tomato = boost::flags::nth_bit(0), // == 0x01
cheese = boost::flags::nth_bit(1), // == 0x02
salami = boost::flags::nth_bit(2), // == 0x04
olives = boost::flags::nth_bit(3), // == 0x08
garlic = boost::flags::nth_bit(4), // == 0x10
all_toppings = tomato | cheese | salami | olives | garlic,
};
// enable Boost.Flags for pizza_toppings
BOOST_FLAGS(pizza_toppings)
enum class ice_cream_flavours {
vanilla = boost::flags::nth_bit(0), // == 0x01
chocolate = boost::flags::nth_bit(1), // == 0x02
strawberry = boost::flags::nth_bit(2), // == 0x04
};
// enable Boost.Flags for ice_cream_flavours
BOOST_FLAGS(ice_cream_flavours)
// order selected pizza
void order_pizza(pizza_toppings toppings) {
std::cout << "Pizza ordered with\n";
if (boost::flags::any(toppings & pizza_toppings::tomato)) {
std::cout << "- tomato\n";
}
if (boost::flags::any(toppings & pizza_toppings::cheese)) {
std::cout << "- cheese\n";
}
if (boost::flags::any(toppings & pizza_toppings::salami)) {
std::cout << "- salami\n";
}
if (boost::flags::any(toppings & pizza_toppings::olives)) {
std::cout << "- olives\n";
}
if (boost::flags::any(toppings & pizza_toppings::garlic)) {
std::cout << "- garlic\n";
}
std::cout << "\n";
}
// order selected dessert
void order_ice_cream(ice_cream_flavours flavours) {
std::cout << "Ice cream ordered with flavours\n";
if (boost::flags::any(flavours & ice_cream_flavours::vanilla)) {
std::cout << "- vanilla\n";
}
if (boost::flags::any(flavours & ice_cream_flavours::chocolate)) {
std::cout << "- chocolate\n";
}
if (boost::flags::any(flavours & ice_cream_flavours::strawberry)) {
std::cout << "- strawberry\n";
}
std::cout << "\n";
}
int main() {
pizza_toppings toppings = pizza_toppings::tomato | pizza_toppings::cheese; // a decent start
toppings |= pizza_toppings::salami | pizza_toppings::olives; // even better
order_pizza(toppings); // order main course
order_pizza(toppings & ~pizza_toppings::salami); // order a vegetarian pizza
order_ice_cream(ice_cream_flavours::vanilla); // order desert
// Guest: "Pizza without olives!"
// Waiter: "Ok, no olives. But what else to put on it?"
// error: negative mask is not a pizza topping
order_pizza(~pizza_toppings::olives);
// Guest: "Pizza with all toppings but olives!"
// Waiter: "Ok, got it!"
// Waiter takes note: Pizza with tomato, cheese, salami, garlic.
order_pizza(pizza_toppings::all_toppings & ~pizza_toppings::olives);
// error: mixing different enumerations
toppings |= ice_cream_flavours::strawberry;
// error: called with wrong enumeration
order_ice_cream(toppings);
}
Why use Boost.Flags
-
provides type-safe bitwise operations for flag-like scoped / unscoped enumerations
-
turns logical errors into type errors
Characteristics of Boost.Flags
-
is non-intrusive - only a macro invocation is required to opt-in
-
single header-file library
-
no dependencies (only standard-library includes)
-
compile-time usable - everything is
constexpr
-
zero-overhead (in optimized builds)
-
operators are found by ADL
-
requires at least C++11, uses newer features if available
Opt-in
To opt-in to Boost.Flags for an enumeration E
the easiest and most comfortable way is to use the macro BOOST_FLAGS
for E
in the same namespace as E
enum class E { ... };
BOOST_FLAGS(E)
Best practice would be writing BOOST_FLAGS(E)
right after its definition / declaration.
In case the enumeration E
is defined inside a class or class template, the macro BOOST_FLAGS_LOCAL
has to be used instead:
class my_class {
// ...
enum class E { ... };
BOOST_FLAGS_LOCAL(E)
// ...
};
Note
|
Inside class definitions,
The reason is, that
|
In case you want to check whether an enumeration is enabled for Boost.Flags, you can test it using boost::flags::enable
(resp. boost::flags::enable_v
for C++14 or later), e.g.
static_assert(boost::flags::enable<E>::value, "Please check if boost/flags.hpp is included and E is enabled correctly.");
Scoped or Unscoped enumerations
The usage of scoped enumeration is strongly recommended, as they prohibit implicit conversion to the underlying integer type and thus provide more type safety. Further, by default scoped enumerations do not pollute the current namespace with the names of their enumerators.
Nevertheless, for both kinds of enumerations Boost.Flags will detect semantical errors when using bitwise operations.
For scoped enumerations the language does not provide built-in bitwise and logical operators, so Boost.Flags will provide them as requested.
For unscoped enumerations the language provides built-in bitwise and logical operators. Hence, Boost.Flags will delete binary operators &
, |
, ^
, &&
, ||
, ==
and !=
if
-
at least one of its arguments is enabled for Boost.Flags
and
-
both arguments are implicitly convertible to an integer type
and
-
for enumeration
E
both arguments are not compatible
The underlying type
In most situations the usage of Boost.Flags does not depend on the enumeration’s underlying type.
Only when Boost.Flags is used with an unscoped enumeration E
in connection with option disable_complement
(i.e. operator~(E) -> E
) then the specification of the underlying type is required to prevent undefined behavior. For details see below.
The long story
Why do we use the bits of integer types for Boolean options?
-
easy to define, e.g. as macros, integer constants or enumerators
-
language support for setting and querying through bitwise operators
-
compact representation in memory
-
simple bulk transfer in interfaces (e.g. on many platforms an
unsigned int
can hold up to 32 different Boolean options) -
enforces the usage of names, especially to prevent
bool
parameters in interfaces
C++ has everything built-in to work with boolean flags, so why do we need Boost.Flags?
Because the language allows to do too much with them!
Type-Safety
For flags based on integer-types or unscoped enumerations all values are implicitly convertible to int
. The compiler can’t help, when we accidentally apply binary operators to unrelated flags.
Here’s an example from the Win32-API:
In WinUser.h
we find the listbox-style
#define LBS_NOTIFY 0x0001L
and the button-style
#define BS_NOTIFY 0x00004000L
If we forget the leading L
of LBS_NOTIFY
and write
if (listbox_styles & BS_NOTIFY) { ... }
instead, we produced syntactically correct code but semantical nonsense.
Switching to enumerations makes it even worse
If the Windows team had decided to use unscoped enumerations instead of macros it wouldn’t have helped: the arguments of built-in bitwise operators are subject to integral promotion, i.e. they forget about the enumeration’s semantic overhead. Even worse:
-
The built-in operators
&
,|
,^
and~
return integer types. Assigning their results to enumeration-type variables require explicit casts, which is another source for errors undetectable by the compiler. -
The built-in operators
&=
,|=
,^=
require the left-hand type to be arithmetic, which excludes enumerations.
Furthermore, scoped enumerations (which didn’t exist when WinUser.h
was created) prohibit implicit integral promotion and therefore don’t work with built-in bitwise operators at all.
But enumerations provide type-safety
On the other hand, enumerations, and especially scoped enumerations, improve the type-safety of our code:
-
variables and arguments of enumeration type can only by assigned (resp. invoked) with a values of the correct type
-
there is no implicit integral promotion for scoped enumerations
With Boost.Flags you can get both
With Boost.Flags we get support for all bitwise operators and type-safety. E.g. the following code
enum class E { a=1, b=2, c=4, d=8 };
BOOST_FLAGS(E)
-
enables all the operators
~
,&
,|
,^
,&=
,|=
,^=
forE
andcomplement<E>
(see below) -
the bitwise negation
~
changes the type fromE
tocomplement<E>
and vice-versa -
the binary operators require both arguments to be from the enumeration resp. its
complement
The underlying type
Here is a standard conforming decision diagram, whether the specification of the underlying type required:

Ok, the unsigned
requirement for pre C++20 compilers is more of a theoretical issue, since all C++ compilers already used two’s complement for signed integers, even before it got mandatory with C++20 (https://en.cppreference.com/w/cpp/language/types).
So, in case your compiler uses signed two’s complement, you can use the following simplified decision diagram:

It may seem pedantic, but defining the underlying type in the unscoped and disable_complement
case is crucial, as otherwise the operator~
will invoke undefined behavior, when its result is not within the value range of the enumeration.
Compilers can track this down, e.g. when evaluating constant expressions (cf. https://eel.is/c++draft/expr.static.cast#8).
Starting with Clang 16 this is diagnosed as a hard error.
Note
|
For unscoped enums with unspecified underlying type, the compiler infers two types:
The deduction of this hypothetical integer value type requires static reflection which is only available in C++26. (It requires to know the values of all enumerators.) Furthermore, it is in general not the same as the underlying type. E.g.
has underlying type |
Complementing the complement
Before going into details, here is a little story:
Once, there was a kingdom, where the Queen of Hearts reigned with iron fist.
She had her own newspaper for proclamations, where they used a set of flags for font-styles
enum font_styles : unsigned int {
bold = 1,
italic = 2,
underline = 4
};
// special style for the Queen's proclamations
static constexpr auto queen_of_hearts_name = italic | underline;
One of the Queen's proclamations had been:
All my proclamations shall be encoded in C++, the Queen!
And there were loads of them every day.
A programmer for the newspaper, who was tired of typing queen_of_hearts_name
all the time, figured out that typing ~bold
instead also did the job. This saved him a lot of work.
One day, the troublemaker Alice came to the kingdom and the Queen ordered the creation of a new font-style
strikeout = 8
which should be used for Alice's name. So it got added to the `font_styles` enumeration.
The next day the following proclamation appeared in the newspaper:
All persons whose names are stricken out are enemies of the kingdom, the Queen!
The last thing the programmer heard, as he ran away from the kingdom, was the queen shouting "Off with his head!".
There are two morals of this tale:
-
a syntactical: the negation flips all bits of the underlying integer. Even though
~bold
did the job in the beginning, it is different fromqueen_of_hearts_name
. -
a semantical:
~bold
isn’t a set of font-modifications. Its purpose is to exclude boldness from a given set of flags.
Even though the underlying type together with its operators &
, |
and ~
denotes a Boolean algebra, when using it as set of flags only the operator &
and |
are endo-functions: the operator ~
returns an element from a complemented set.
So, inside the Boolean algebra of the underlying type, there exist two sets of flags, where each of them resembles the semantics of mathematical lattices and both are entangled by the operator ~
.
Hence, for a Boost.Flags enabled enumeration E
(where complement is not disabled) the bitwise not operator switches between the types E
and complement<E>
-
operator~(E) -> complement<E>
-
operator~(complement<E>) -> E
We will call arguments for binary operators compatible if the Boost.Flags provided operators are viable, i.e. each one is implicitly convertible to E
or complement<E>
. Additionally, for operator==
and operator!=
both have to be convertible to E
or both have to be convertible to complement<E>
. For assignment operators additional requirements apply (cf. Operators).
Mathematical justification for complement
This section provides a mathematical explanation, why complement
is required. It can easily be skipped.
-
The underlying type
U
with the bitwise operations~, &, |
and the constants0
and-1
(all bits set) form a (bitcount of U)-dimensional Boolean algebra (U, 0, -1, ∼, &, ∣) -
The defined flags (usually given by the single-bit enumerators) with the bitwise operations
&, |
form a substructure (F, &, ∣) of U (cf. mathematical lattices) which in general is not closed under bitwise negation~
. -
Building the closure F of F wrt. ∼ generates a Boolean algebra which is a Boolean subalgebra of U.
Semantically, the elements of F∖ F are not combinations of flags but negative flag-masks. The distinction of E
and complement<E>
keeps them apart on the type-level.
Finally, for the binary operators we have
-
operator&
-
operator&(E, E) -> E
-
operator&(complement<E>, E) -> E
-
operator&(E, complement<E>) -> E
-
operator&(complement<E>, complement<E>) -> complement<E>
-
-
operator|
-
operator|(E, E) -> E
-
operator|(complement<E>, E) -> complement<E>
-
operator|(E, complement<E>) -> complement<E>
-
operator|(complement<E>, complement<E>) -> complement<E>
-
-
operator^
-
operator^(E, E) -> E
-
operator^(complement<E>, E) -> complement<E>
-
operator^(E, complement<E>) -> complement<E>
-
operator^(complement<E>, complement<E>) -> E
-
Which means, on the meta-level the typeset {E, complement<E>} and the operation ∼, & and ∣ form a two-element Boolean algebra.
For the rare cases where
-
the set of flags semantically form a Boolean algebra and additionally
-
all bits of the underlying type correspond to flags
there exists the option disable_complement
which disables the usage of complement
and sets operator~(E) -> E
.
From flags to bool
Since scoped enumerations prevent implicit conversion to bool
, Boost.Flags provides the following functions:
-
any(e) -> bool
: equivalent toe != E{}
-
none(e) -> bool
: equivalent toe == E{}
Furthermore, to test for intersection and entailment of flag-sets:
-
intersect(e1, e2) -> bool
: equivalent toe1 & e2 != E{}
-
disjoint(e1, e2) -> bool
: equivalent toe1 & e2 == E{}
-
subseteq(e1, e2) -> bool
: equivalent toe1 & e2 == e1
-
subset(e1, e2) -> bool
: equivalent tosubseteq(e1, e2) && e1 != e2
Instead of calling any
and none
we can use operator!
-
!e
: equivalent tonone(e)
-
!!e
: equivalent toany(e)
Flags and
Flags
Instead of disjoint
and intersect
we can write:
-
!(e1 & e2)
: equivalent todisjoint(e1, e2)
-
!!(e1 & e2)
: equivalent tointersect(e1, e2)
If we update existing code with Boost.Flags, then expressions like e1 & e2
in Boolean contexts would have to be replaced by !!(e1 & e2)
. This is tedious and requires adding parenthesis.
Therefore, Boost.Flags provides a pseudo operator BOOST_FLAGS_AND
with the same precedence and associativity as &
but returning a bool
:
-
e1 BOOST_FLAGS_AND e2
: equivalent to!!(e1 & e2)
Alternatively, there is the option BOOST_FLAGS_LOGICAL_AND
, which enables operator&&
, see next section.
Note
|
|
Logical and
and or
The logical operator&&
and operator||
are special among overloadable operators in C++: their built-in versions use short-circuit evaluation, but overloaded versions behave like regular functions and always evaluate both arguments.
Furthermore, for integer and (non Boost.Flags enabled) unscoped enumeration-typed expressions a
and b
, both (a) & (b)
and (a) && (b)
(resp. (a) | (b)
and (a) || (b)
) are all syntactically valid expressions but with different semantics
-
(a) & (b)
(resp.(a) | (b)
) always evaluatesb
-
(a) & (b)
and(a) && (b)
(resp.(a) | (b)
and(a) || (b)
) have different result-types
Even in Boolean contexts (a) & (b)
and (a) && (b)
return different results (e.g. for arguments 1
and 2
).
To be on the safe side, Boost.Flags by default deletes operator&&
and operator||
for enabled enumerations. When applied to existing code, this will lead to compilation errors and enforces revisiting their uses.
Nevertheless, sometimes operator&&
comes in handy or is even required, e.g. when Boost.Flags is used together with other generic libraries. If for an enumeration E
BOOST_FLAGS(E, BOOST_FLAGS_LOGICAL_AND)
// resp. for a class local enum
BOOST_FLAGS_LOCAL(E, BOOST_FLAGS_LOGICAL_AND)
is defined, then operator&&
is overloaded for E
with the semantics
operator&&(e1, e2) -> bool { return !!(e1 & e2); }
and there is no short-circuit evaluation!
Warning
|
When applying Boost.Flags to existing code, please be cautious enabling
So, before enabling |
Note
|
To achieve short-circuit evaluation for expressions with flags, first convert the flag-value arguments to |
Calls to order
Let’s take a look at the relational operators <
, <=
, >
, >=
and <=>
.
For integer-based types, including scoped and unscoped enumerations, C++ provides built-in relational operators which apply the operator to the values (of the underlying type in case of enumerations). The induced order is a total: for any two values exactly one of the operators <
, ==
, >
evaluates to true
.
These total orders are syntactically important, as we normally use them as Compare predicate for sorted containers and sorting algorithms.
But semantically, these orders do not carry much information: for the (introducing example), the meaning of
pizza_toppings::cheese | pizza_toppings::salami < pizza_toppings::olives
reduces to some information about, which bits of the underlying type might be used by the different options. But this is something we wanted to abstract away in the first place. So, there is a dilemma: on the one hand, we require a total order for sorted containers and sorting algorithms. But on the other hand, the use of this order in other places may indicate logical errors and should be prohibited.
To handle this Boost.Flags provides the following macros:
-
BOOST_FLAGS_DELETE_REL(E)
: deletes all relational operators for enumerationE
-
BOOST_FLAGS_SPECIALIZE_STD_LESS(E)
: specializesstd::less
for enumerationE
to use the total order induced by the underlying values
The recommendation is defining both BOOST_FLAGS_DELETE_REL(E)
and BOOST_FLAGS_SPECIALIZE_STD_LESS(E)
as it allows standard sorting and sorted containers for E
while disabling usage of relational operators in user code.
Note
|
In case So, to use range-algorithms in this case we have to provide the sort-predicate explicitly, which can be either |
Utilities
Functions
All following utility functions are defined in namespace boost::flags
. For an enabled enumeration E
Boost.Flags provides the following functions:
Name |
Type |
Definition |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The macro BOOST_FLAGS_USING_UTILITIES()
imports all utility functions, except from_underlying
, through using-directives into the current namespace.
For generating the values of enumerators Boost.Flags provides furthermore:
-
template<typename T = int> nth_bit(unsigned int n) -> underlying_or_identity<T>::type
returning the n-th power of 2 -
template<typename T> next_bit(T v) -> underlying_or_identity<T>::type
returning the next power of 2 (if applied to a power of 2)
where type T
can be either an enumeration or an integral type, and underlying_or_identity
is a type-trait returning the underlying type of the enumeration or is the type-identity respectively
Other flags-like enum implementations
There are several articles and libraries on flag-like enums available. The following list is not exhaustive:
-
https://www.boost.org/doc/libs/1_84_0/boost/detail/bitmask.hpp (BOOST_BITMASK, BB)
-
https://www.sandordargo.com/blog/2022/06/22/bitwise-enums (SD)
-
https://m-peko.github.io/craft-cpp/posts/different-ways-to-define-binary-flags/ (MP)
-
https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-flagsattribute (.net, language built-in flags support - not usable with ISO-C++ !)
Boost.Flags | BB | SD | GR | MP | Qt | JM | .net | |
---|---|---|---|---|---|---|---|---|
non-intrusive |
+ |
+ |
+ |
- |
- |
- |
- |
+ |
zero-overhead |
+ |
+ |
+ |
+ |
- |
- |
+ |
+ |
distinguishes complement |
+ |
- |
- |
- |
- |
- |
+ |
- |
operators as free functions |
+ |
+ |
+ |
- |
- |
- |
- |
+ |
everything is constexpr |
+ |
- |
- |
- |
- |
- |
- |
N/A |
unscoped type safety (*) |
+ |
- |
- |
+ |
+ |
- |
+ |
+ |
available as library |
+ |
+ |
- |
+ |
+ |
+ |
+ |
+ |
(*) unscoped type safety: The library prevents usage of built-in binary operators, when both arguments are implicitly convertible to an integer type and at least one of the arguments is an enabled enumeration type.
Design rationale
The design of Boost.Flags was driven by the following principles:
-
type-safety
-
the library shall enforce that binary operators can only be applied to flags (resp. their complements) from the same enumeration
-
flag values shall be distinguishable from their complements on the type-level
-
-
non-intrusive
-
as few as possible changes to the definition of existing flag-like enumeration shall be required
-
do not introduce new types when using flags on enumerations, except the
complement
template
-
-
opt-in shall be done by
-
by simple macro invocations
-
-
zero-overhead
-
for optimized builds operator-calls must produce the same assembly code as built-in calls on the underlying integers
-
-
no change of semantics for existing code
-
enabling Boost.Flags for an existing flag-like enumeration must not produce valid code with different semantics
-
erroneous usage of binary operators (e.g. applying operators to different enumerations) shall produce compilation errors
-
The first four principles are met by the library. The last principle of course has a notable exception:
For a given enumeration E
values of type complement<E>
may invoke different overloads than values of E
.
Revision History
Changes in 1.??.0
-
Added to Boost
Reference
The contents of the library are in namespace boost::flags.
All operators and functions in this library
-
are
constexpr
andnoexcept
-
use attribute
nodiscard
except for operators / functions returning a reference to one of its arguments
At most places they are left out for readability. Furthermore, many function signatures for operators are written with trailing return type, where the leading auto
is also left out for readability.
Supported language versions
Boost.Flags requires at least C++11.
Concepts are used if available and not disabled by BOOST_FLAGS_HAS_CONCEPTS
.
Macros to opt-in
BOOST_FLAGS
For an enumeration E
at namespace scope the following variadic macro enables all operators of Boost.Flags
BOOST_FLAGS(E)
resp.
BOOST_FLAGS(E, OPTIONS)
where OPTIONS is an optional comma separated list containing at most one of each of the following options:
-
BOOST_FLAGS_LOGICAL_AND
: additionally enablesoperator&&
forE
(see alsological_and
) -
BOOST_FLAGS_DISABLE_COMPLEMENT
: disables the usage ofcomplement<E>
(see alsodisable_complement
) -
BOOST_FLAGS_NO_FORWARDING
: will not generate forwardingfriend
operators when using BOOST_FLAGS_LOCAL (has no effect for BOOST_FLAGS) (see also Opt-in andBOOST_FLAGS_FORWARD_OPERATORS
)
furthermore at most one of the following relational-operator options may be specified:
-
BOOST_FLAGS_DEFAULT_REL
: use operators <, <=, >, >= and <=> which accept two compatible flag-arguments and compare their underlying values (cf.BOOST_FLAGS_SPECIALIZE_STD_LESS
) -
BOOST_FLAGS_DELETE_REL
: delete operators <, <=, >, >= and <=> for type E (cf.BOOST_FLAGS_DELETE_REL
)
If no relational-operator option is specified, the macro defaults to using BOOST_FLAGS_DEFAULT_REL
.
Note
|
When errors are encountered using
|
BOOST_FLAGS(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()
).
BOOST_FLAGS_LOCAL
For an enumeration E
at class scope use the following variadic macro to enables all operators of Boost.Flags
BOOST_FLAGS_LOCAL(E)
resp.
BOOST_FLAGS_LOCAL(E, OPTIONS)
enables all operators of Boost.Flags.
For the description of OPTIONS
please refer to BOOST_FLAGS.
By default BOOST_FLAGS_ENABLE_LOCAL(E)
creates friend functions for all operators of Boost.Flags for the enumeration E
(cf. BOOST_FLAGS_FORWARD_OPERATORS_LOCAL(E)).
Non-variadic macros for enabling
The following macros are primarily provided for non standard conforming preprocessors.
BOOST_FLAGS_ENABLE
For an enumeration E
at namespace scope the macro
BOOST_FLAGS_ENABLE(E)
enables all operators of Boost.Flags.
BOOST_FLAGS_ENABLE(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_LOGICAL_AND
For an enumeration E
at namespace scope the macro
BOOST_FLAGS_ENABLE_LOGICAL_AND(E)
enables all operators of Boost.Flags and enables operator&&
for E
(see also logical_and
)
BOOST_FLAGS_ENABLE_LOGICAL_AND(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT
For an enumeration E
at namespace scope the macro
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT(E)
enables all operators of Boost.Flags and disables the usage of complement<E>
(see also disable_complement
)
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT_LOGICAL_AND
For an enumeration E
at namespace scope the macro
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT_LOGICAL_AND(E)
enables all operators of Boost.Flags,disables the usage of complement<E>
(see also disable_complement
) and enables operator&&
for E
(see also logical_and
)
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT_LOGICAL_AND(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_LOCAL
For an enumeration E
at class scope the macro
BOOST_FLAGS_ENABLE_LOCAL(E)
enables all operators of Boost.Flags.
BOOST_FLAGS_ENABLE_LOCAL(E)
creates friend functions for all operators of Boost.Flags for the enumeration E
(cf. BOOST_FLAGS_FORWARD_OPERATORS_LOCAL(E)).
BOOST_FLAGS_ENABLE_LOCAL_LOGICAL_AND
For an enumeration E
at class scope the macro
BOOST_FLAGS_ENABLE_LOCAL_LOGICAL_AND(E)
enables all operators of Boost.Flags and enables operator&&
for E
(see also logical_and
)
BOOST_FLAGS_ENABLE_LOCAL_LOGICAL_AND(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_DISABLE_COMPLEMENT
For an enumeration E
at class scope the macro
BOOST_FLAGS_ENABLE_LOCAL_DISABLE_COMPLEMENT(E)
enables all operators of Boost.Flags and disables the usage of complement<E>
(see also disable_complement
)
BOOST_FLAGS_ENABLE_LOCAL_DISABLE_COMPLEMENT(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
BOOST_FLAGS_ENABLE_LOCAL_DISABLE_COMPLEMENT_LOGICAL_AND
For an enumeration E
at class scope the macro
BOOST_FLAGS_ENABLE_LOCAL_DISABLE_COMPLEMENT_LOGICAL_AND(E)
enables all operators of Boost.Flags,disables the usage of complement<E>
(see also disable_complement
) and enables operator&&
for E
(see also logical_and
)
BOOST_FLAGS_ENABLE_LOCAL_DISABLE_COMPLEMENT_LOGICAL_AND(E)
imports all operators of Boost.Flags into the current namespace (cf. BOOST_FLAGS_USING_OPERATORS()).
Enabling helper functions, types and macros
Note
|
The functions, types and macros in this section are usually not used directly, but implicitly through one of the |
BOOST_FLAGS_USING_OPERATORS
The macro BOOST_FLAGS_USING_OPERATORS()
imports all operators from namespace boost::flags
into the current namespace.
This will enable ADL for Boost.Flags operators for enumerations in the current namespace.
BOOST_FLAGS_USING_UTILITIES
The macro BOOST_FLAGS_USING_UTILITIES()
imports all utility functions from namespace boost::flags
into the current namespace.
This will enable ADL for Boost.Flags utility functions for enumerations in the current namespace.
BOOST_FLAGS_USING_ALL
The macro BOOST_FLAGS_USING_ALL()
is a shorthand for
BOOST_FLAGS_USING_OPERATORS()
BOOST_FLAGS_USING_UTILITIES()
BOOST_FLAGS_FORWARD_OPERATORS
For an enumeration E
the macro BOOST_FLAGS_FORWARD_OPERATORS(E)
creates forwarding functions for all Boost.Flags operators for the enumeration E
in the current namespace.
BOOST_FLAGS_FORWARD_OPERATORS_LOCAL
For an enumeration E
the macro BOOST_FLAGS_FORWARD_OPERATORS_LOCAL(E)
creates forwarding friend functions for all Boost.Flags operators for the enumeration E
in the current class / class template.
Note
|
Since the language does not allow |
boost_flags_enable
The function boost_flags_enable
can be overloaded for an enumeration E
either
-
with return-type
std::integral_constant<bool, true>
:constexpr std::integral_constant<bool, true> boost_flags_enable(E) { return {}; }
enables Boost.Flags for an enumeration
E
. All optional features are disabled. -
or with return-type
template<boost::flags::options Opts> boost::flags::options_constant
, where the enumerationboost::flags::options
has the following options:-
enable
: enablesE
-
disable_complement
: disables the usage ofcomplement<E>
(see alsodisable_complement
) -
logical_and
: enablesoperator&&
forE
(see alsological_and
) e.g.constexpr boost::flags::options_constant< boost::flags::options::enable // enable E (required) | boost::flags::options::disable_complement // disable the usage of the complement | boost::flags::options::logical_and // enable operator&& > boost_flags_enable(E) { return {}; }
-
The function boost_flags_enable
is looked up using ADL.
In case E
is defined inside a class, a friend
function can be used for enabling. E.g.
class my_class {
// ...
enum class E:unsigned int { ... };
friend constexpr bool boost_flags_enable(E) { return true; }
// ...
};
enable<T>
Alternatively to boost_flags_enable
an enumeration can be enabled by specializing the following template for an enumeration
template<typename T> struct enable : std::false_type {};
and inherit from std::true_type
.
Example:
enum class my_flags : unsigned int {
option_a = 0x1,
option_b = 0x2,
};
template<> struct boost::flags::enable<my_flags> : std::true_type {};
When boost::flags::enable
is specialized for E
any definition of boost_flags_enable
for E
are ignored
(cf. boost_flags_enable
).
In case variable templates are available (cf. BOOST_FLAGS_HAS_VARIABLE_TEMPLATES
) then
template<typename E>
constexpr bool enable_v = enable<E>::value;
is also provided.
logical_and
If the specialization of enable
additionally inherits from logical_and
then operator&&
is enabled.
Example:
template<> struct boost::flags::enable<E>
: std::true_type
, boost::flags::logical_and
{};
(cf. BOOST_FLAGS_LOGICAL_AND
)
disable_complement
If the specialization of class template enable
additionally inherits from disable_complement
then the usage of the complement
template (cf. operator~
) is disabled.
When disable_complement
is used, then the used enumeration must either be scoped, or has an explicit specified underlying type, otherwise the operator~
may invoke undefined behavior (cf. The underlying type and BOOST_FLAGS_DISABLE_COMPLEMENT
).
Types
complement<T>
The template complement
indicates that a value is the bitwise negation of a flag-value.
It is used to distinguish flag-values from negative masks.
template<typename T> struct complement;
By default the usage of complement
is enabled, but can be disabled using BOOST_FLAGS_DISABLE_COMPLEMENT
.
total_order_t
The type boost::flags::total_order_t
defines a call-operator which accepts two compatible flag-arguments and compares their underlying values.
There exists also a function object total_order
:
static constexpr boost::flags::total_order_t total_order;
When BOOST_FLAGS_DELETE_REL
is specified, then total_order
can be used as Compare-predicate for range-algorithms.
Operators
operator~
Reverses all bits of the underlying integer representation of its argument.
The signature of operator~
depends on whether complement
is enabled (cf. BOOST_FLAGS_DISABLE_COMPLEMENT).
When complement
is enabled for flags E
-
operator~(E) -> complement<E>
-
operator~(complement<E>) -> E
otherwise
-
operator~(E) -> E
operator&
Applies a bitwise AND operation on the underlying integer representations of its arguments.
The signature of operator&
depends on whether complement
is enabled (cf. BOOST_FLAGS_DISABLE_COMPLEMENT).
When complement
is enabled for flags E
-
operator&(E, E) -> E
-
operator&(complement<E>, E) -> E
-
operator&(E, complement<E>) -> E
-
operator&(complement<E>, complement<E>) -> complement<E>
otherwise
-
operator&(E, E) -> E
All other viable operator&
where at least one of the arguments is enabled for Boost.Flags and both are implicitly convertible to an integer type are deleted.
operator|
Applies a bitwise OR operation on the underlying integer representations of its arguments.
The signature of operator|
depends on whether complement
is enabled (cf. BOOST_FLAGS_DISABLE_COMPLEMENT).
When complement
is enabled for flags E
-
operator|(E, E) -> E
-
operator|(complement<E>, E) -> complement<E>
-
operator|(E, complement<E>) -> complement<E>
-
operator|(complement<E>, complement<E>) -> complement<E>
otherwise
-
operator|(E, E) -> E
All other viable operator|
where at least one of the arguments is enabled for Boost.Flags and both are implicitly convertible to an integer type are deleted.
operator^
Applies a bitwise XOR operation on the underlying integer representations of its arguments.
The signature of operator^
depends on whether complement
is enabled (cf. BOOST_FLAGS_DISABLE_COMPLEMENT).
When complement
is enabled for flags E
-
operator^(E, E) -> E
-
operator^(complement<E>, E) -> complement<E>
-
operator^(E, complement<E>) -> complement<E>
-
operator^(complement<E>, complement<E>) -> E
otherwise
-
operator^(E, E) -> E
All other viable operator^
where at least one of the arguments is enabled for Boost.Flags and both are implicitly convertible to an integer type are deleted.
operator&=
Performs a bitwise AND assignment on the underlying integer representations of its arguments.
The signature of operator&=
depends on whether complement
is enabled (cf. BOOST_FLAGS_DISABLE_COMPLEMENT).
When complement
is enabled for flags E
-
operator&=(E&, E) -> E&
-
operator&=(E&, complement<E>) -> E&
-
operator&=(complement<E>&, complement<E>) -> complement<E>&
Note
|
The assignment |
otherwise
-
operator&=(E&, E) -> E&
operator|=
Performs a bitwise OR assignment on the underlying integer representations of its arguments.
The signature of operator|=
depends on whether complement
is enabled (cf. BOOST_FLAGS_DISABLE_COMPLEMENT).
When complement
is enabled for flags E
-
operator|=(E&,E) -> E&
-
operator|=(complement<E>&,E) -> complement<E>&
-
operator|=(complement<E>&,complement<E>) -> complement<E>&
Note
|
The assignment |
otherwise
-
operator|=(E&,E) -> E&
operator^=
Performs a bitwise XOR assignment on the underlying integer representations of its arguments.
The signature of operator^=
depends on whether complement
is enabled (cf. BOOST_FLAGS_DISABLE_COMPLEMENT).
When complement
is enabled for flags E
-
operator^=(E&,E) -> E&
-
operator^=(complement<E>&,E) -> complement<E>&
Note
|
The assignment |
otherwise
-
operator^=(E&,E) -> E&
operator!
The operator!
tests if a value is empty.
// pseudo code
bool operator!(E e) { return e == E{}; }
Pseudo operator BOOST_FLAGS_AND
The pseudo operator BOOST_FLAGS_AND
computes the bitwise AND of its arguments and converts the result to bool
.
// pseudo code
bool operator BOOST_FLAGS_AND(E e1, E e2) { return !!(e1 & e2); }
BOOST_FLAGS_AND
has same precedence and associativity as operator&
. It is a macro defined as
#define BOOST_FLAGS_AND & boost::flags::impl::pseudo_and_op_tag{} &
operator== , operator!=
The description is provided only for operator==
. Calls with reversed arguments and calls to operator!=
are also available and return the respective results.
-
operator==(E, E) -> bool
-
operator==(complement<E>, complement<E>) -> bool
equality is defined as usual by applying the operator to the underlying integer.
Furthermore the following overloads are defined
-
operator==(E, std::nullptr_t) -> bool
-
operator==(E, boost::flags::null_tag) -> bool
Both test for equality with an underlying value of 0
. The macro BOOST_FLAGS_NULL
defines an instance of boost::flags::null_tag
.
All other viable operator==
and operator!=
where at least one of the arguments is enabled for Boost.Flags and both are implicitly convertible to an integer type are deleted.
Note
|
If
compiles and returns the expected result. But, in case |
Utility functions
any
// pseudo code
bool any(E e) { return e != E{}; }
Tests if a value is not empty.
none
// pseudo code
bool none(E e) { return e == E{}; }
Tests if a value is empty.
intersect
// pseudo code
bool intersect(E e1, E e2) { return e1 & e2 != E{}; }
Tests if two values have at least one common bits set.
disjoint
// pseudo code
bool disjoint(E e1, E e2) { return e1 & e2 == E{}; }
Tests if two values do not have a common bit set.
subseteq
// pseudo code
bool subseteq(E e1, E e2) { return e1 & e2 == e1; }
Tests if all bits set in the first argument are also set in the second argument.
subset
// pseudo code
bool subset(E e1, E e2) { return subseteq(e1, e2) && (e1 != e2); }
Tests if the bits set in the first argument are a proper subset of the bits in the second argument.
make_null
// pseudo code
E make_null(E) { return E{}; }
For flags E
returns an empty instance of type E
, i.e. with underlying value 0
.
make_if
// pseudo code
E make_if(E e, bool set) { return set ? e : E{}; }
Depending on set
returns either the first argument or empty an instance of type E
.
add_if
// pseudo code
E add_if(E e, E mod, bool add) { return add ? e | mod : e; }
Depending on add
either adds all bits from mod
to e
or leaves e
unmodified and returns the result.
add_if_inplace
// pseudo code
E& add_if_inplace(E& e, E mod, bool add) { e = add_if(e, mod, add); return e; }
Similar to add_if
but applies the modification to e
and returns it as reference.
remove_if
// pseudo code
E remove_if(E e, E mod, bool remove) { return remove ? e & ~mod : e; }
Depending on remove
either removes all bits from mod
from e
or leaves e
unmodified and returns the result.
remove_if_inplace
// pseudo code
E& remove_if_inplace(E& e, E mod, bool remove) { e = remove_if(e, mod, remove); return e; }
Similar to remove_if
but applies the modification to e
and returns it as reference.
modify
// pseudo code
E modify(E e, E mod, bool set) { return set ? e | mod : e & ~mod; }
Depending on set
either adds or removes all bits from mod
to e
and returns the result.
modify_inplace
// pseudo code
E& modify_inplace(E& e, E mod, bool set) { e = modify(e, mod, set); return e; }
Similar to modify
but applies the modification to e
and returns it as reference.
get_underlying
Returns the underlying value.
Let U
be the underlying type of enabled enum E
// pseudo code
U get_underlying(E e) { return static_cast<U>(e); }
from_underlying
Casts an value from underlying value the an enabled enum.
Let U
be the underlying type of enabled enum E
// pseudo code
E from_underlying(U u) { return static_cast<E>(u); }
nth_bit
// pseudo code
template<typename T = int>
underlying_or_identity<T>::type nth_bit(unsigned int n) {
return static_cast<underlying_or_identity<T>::type>(1) << n;
}
Returns a value of type T
with the n-th bit from the right set (zero-based) set.
The type T
can be either an enumeration or an integral type, and underlying_or_identity
is a type-trait returning the underlying type of the enumeration or it is the type-identity respectively.
next_bit
// pseudo code
template<typename T>
T next_bit(T v) { return v << 1; }
Returns to a value v
of type T
the value with the next higher bit set (if available).
The behavior is undefined if more than one bit is set in value v
.
Macros
BOOST_FLAGS_NULL
The macro BOOST_FLAGS_NULL
can be used for (in-)equality testing with a flag-value.
For any value e
of type flags E
the expression e == BOOST_FLAGS_NULL
is equivalent to e == E{}
.
Similarly for e != BOOST_FLAGS_NULL
, BOOST_FLAGS_NULL == e
and BOOST_FLAGS_NULL != e
.
BOOST_FLAGS_DELETE_REL
The macro BOOST_FLAGS_DELETE_REL(E)
deletes all relational operators for a Boost.Flags enabled enumeration E
.
It must be defined at global namespace.
BOOST_FLAGS_SPECIALIZE_STD_LESS
The macro BOOST_FLAGS_SPECIALIZE_STD_LESS(E)
specialize std::less for E and complement<E> with the total order based on the value of the underlying integer type (i.e. <
on the underlying integer type).
It must be defined at global namespace.
The definition of BOOST_FLAGS_SPECIALIZE_STD_LESS(E)
will not enable range algorithms to use that total order. Here it is required to explicitly specify total_order
as compare object.
Library configuration macros
If not specified, all the following macros are deduced from the system, compiler, the C++ version and feature-test. (Boost.Flags is tested with all major compilers on linux (ubuntu), macos and windows.)
BOOST_FLAGS_HAS_THREE_WAY_COMPARISON
Specifies, if three way comparison (⇐>) is available.
Possible values: 0 or 1
BOOST_FLAGS_HAS_PARTIAL_ORDERING
Specifies, if std::partial_ordering
is available.
Possible values: 0 or 1
BOOST_FLAGS_HAS_CONCEPTS
Specifies, if the compiler supports concepts.
Possible values: 0 or 1
BOOST_FLAGS_HAS_IS_SCOPED_ENUM
Specifies, if std::is_scoped_enum
is available.
Possible values: 0 or 1
BOOST_FLAGS_HAS_LOGICAL_TRAITS
Specifies, if the logical traits std::conjunction
, std::disjunction
and std::negation
are available.
Possible values: 0 or 1
BOOST_FLAGS_HAS_REWRITTEN_CANDIDATES
Specifies, if rewitten candidates are available.
Possible values: 0 or 1
BOOST_FLAGS_HAS_VARIABLE_TEMPLATES
Specifies, if variable templates are available.
Possible values: 0 or 1
BOOST_FLAGS_ATTRIBUTE_NODISCARD
Text used as nodiscard attribute (e.g. [[nodiscard]]
).
BOOST_FLAGS_ATTRIBUTE_NODISCARD_CTOR
Text used as nodiscard attribute for constructors (e.g. [[nodiscard]]
).
Appendix A: Copyright and License
This documentation is
-
Copyright 2024, 2025 Tobias Loew
-
Distributed under the Boost Software License, Version 1.0.
Many thanks to Andreas Buhr, Thomas Forell and Thimo Neubauer for many comments and suggestions.