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_ENABLE(E)

in the same namespace as E.

#include <boost/flags.hpp>

enum class pizza_toppings : unsigned int {
    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_ENABLE(pizza_toppings)

enum class ice_cream_flavours : unsigned int {
    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_ENABLE(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 undetected logical errors into type errors

Characteristics of Boost.Flags

  • is non-intrusive - only a function overload or a template specializations required to opt-in

  • single header-file library

  • no dependencies (only standard-library includes)

  • everything is constexpr

  • zero-overhead in optimized builds

  • operator 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_ENABLE for E in the same namespace as E

BOOST_FLAGS_ENABLE(E)

Best practice would be writing BOOST_FLAGS_ENABLE(E) right after the enumeration in the same namespace.

In case the enumeration E is defined inside a class or class template, the macro BOOST_FLAGS_ENABLE_LOCAL has to be used for enabling:

class my_class {
    // ...
    enum class E:unsigned int { ... };
    BOOST_FLAGS_ENABLE_LOCAL(E)
    // ...
};

In case you want to ensure that an enumeration is enabled for Boost.Flags, you can test it e.g. with

static_assert(boost::flags::enable<E>::value, "Please check if E is enabled correctly and boost/flags.hpp is included.");

scoped or unscoped enumerations?

The usage of scoped enumeration is strongly recommended, as they provide more type safety than unscoped by prohibiting implicit conversion to the underlying integer type.

Nevertheless, for both kinds of enumerations Boost.Flags will detect semantical errors when using bitwise operations.

For unscoped enumerations Boost.Flags will delete binary operators &, |, ^, &&, ||, == and != if

  • at least one is enabled for Boost.Flags and

  • both arguments are implicitly convertible to an integer type and

  • both arguments are not compatible (they are not the same enumeration or its complement)

i.e. Boost.Flags will prohibit that in those cases the built-in version will be used.
(For scoped enumerations this is not necessary, as those cases cannot occur.)

What about the underlying type?

  • For language standards before C++20 the underlying type is required as an unsigned type, for both scoped and unscoped enumerations!

  • For unscoped enumerations with the disable_complement option the underlying type is always required!

(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 (on many platforms an int can hold up to 32 different Boolean options)

  • enforces the usage of names, especially to prevent bool parameters in interfaces:

    Boolean arguments loudly declare that the function does more than one thing. They are confusing and should be eliminated.
    — C. Martin (Uncle Bob)
    Clean Code

C++ has everything needed to work with flags, so why do we need Boost.Flags?

Because we can do too much with them!

Type-Safety

For flags based on integer-types or 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.

  • 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 ones, improve the type-safety of our code:

  • variables and arguments of enumeration type can only by assigned (resp. invoked) with a value of the same type

  • 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 : unsigned int { a=1, b=2, c=4, d=8 };
BOOST_FLAGS_ENABLE(E)
  • enables all the operators ~, &, |, ^, &=, |=, ^= for E and complement<E> (see below)

  • the binary operators require both arguments to be from the enumeration or its complement

  • the bitwise negation ~ changes the type from E to complement<E> and vice-versa

The underlying type

Is the the specification of the underlying type required?

First, here the standard conforming decision diagram:

underlying type decision diagram conforming

Ok, the unsigned is more 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). Furthermore, scoped enumerations always have a fixed underlying type, which is int if not specified (https://eel.is/c++draft/dcl.enum#5).

So, in case your compiler uses signed two’s complement, you can use the following dicision diagram:

underlying type decision diagram practical

It may seem pedantic, but defining the underlying type in the unscoped and disable_complement case is crucial, as otherwise the operator ~ will invoke UB.
Compilers can track this down, e.g. when evaluating constant expressions (cf. https://eel.is/c++draft/expr.static.cast#10).
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 underlying type (https://eel.is/c++draft/dcl.enum#7), which we can query using std::underlying_type

  • a hypothetical integer value type with minimal width such that all enumerators can be represented (https://eel.is/c++draft/dcl.enum#8)
    "The width of the smallest bit-field large enough to hold all the values of the enumeration type […​]."

This hypothetical integer value type is not deducible from the type of the enumeration. (It requires to know the values of all enumerators.) Furthermore, it is in general not the same as the underlying type. E.g.

enum TriBool {
    false_ = 0,
    true_ = 1,
    undefined = 2
};

has underlying type int on all major compilers, but its valid values are just 0, 1, 2 and 3 as the hypothetical integer value type with minimal width is a 2-bit unsigned integer.

Complementing the complement

Before going into details, let me tell you a little tale

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 striken 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 from queen_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.

In a nutshell: the following two operations on sets of flags return another set of flags

  • conjunction (operator&): taking the flags that appear in all sets

  • disjunction (operator|): taking the flags that appear in any of the sets

but negation (operator~) in general does not.
Sets of flags resemble the semantics of mathematical lattices rather than the semantics of a Boolean algebra.

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 constants 0 and -1 (all bits set) form a (bitcount of U)-dimensional Boolean algebra (U, 0, -1, ∼, &, ∣)

  • The defined flags (e.g. bold, italic etc.) 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 case where

  • the set of flags semantically forms 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 (see also operator!):

  • any(e) -> bool : equivalent to e != E{}

  • none(e) -> bool : equivalent to e == E{}

Furthermore, to test for intersection and entailment of flag-sets:

  • intersect(e1, e2) -> bool : equivalent to e1 & e2 != E{}

  • disjoint(e1, e2) -> bool : equivalent to e1 & e2 == E{}

  • subseteq(e1, e2) -> bool : equivalent to e1 & e2 == e1

  • subset(e1, e2) -> bool : equivalent to subseteq(e1, e2) && e1 != e2

not, not not, why not?

Instead of calling any and none we can use operator!

  • !e : equivalent to none(e)

  • !!e : equivalent to any(e)

Everything in order?

Let’s take a look at the relational operators <, <=, > and >=.

For enumerations (scoped and unscoped) C++ provides built-in relational operators which apply the operator to the values of the underlying type. This order we usually want to use as Compare predicate for sorted containers and sorting algorithms.

This total order, is a linearization of a partial order which naturally arises, when we abstract away from the underlying type (which is what enumerations are usually about). It is the order induced by flag entailment:

e1 < e2 if and only if subset(e1, e2) == true

But, if there is more than one flag, then this order is inherently partial and should never be used as Compare predicate for containers or algorithms.

As there are applications for two different orders on the same type, the best would be different sets of relational operators, but C++ offers only one such.
One approach would be, deleting all relational operators for flags and forcing the user to call a function / pass a Compare type as predicate.

But here comes the next obstacle from the language:

Note

C++ allows overloading operators when at least one of its arguments is of class or enum type

enum E{};
bool operator<(E,E){ return true; }
bool test(){
    return E{} < E{};       // calls our operator< and returns true
}

This also works for operator templates, but there is one exception:
When an operator template for a relational operator is invoked with the same enumeration type for both arguments, then the built-in operator is called.

enum E{};
template<typename T1, typename T2> bool operator<(T1,T2){ return true; }
bool test(){
    return E{} < E{};       // calls built-in < and returns false
}

There is a Defect Report pending https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2730 which would make the latter also call the user defined operator template.

Currently, we simply cannot overload relational operators for Boost.Flags enabled enumerations with a function template - it requires the definition of a function. Thus Boost.Flags provides the following macros

  • BOOST_FLAGS_REL_OPS_DELETE(E): deletes all relational operators for enumeration E

  • BOOST_FLAGS_REL_OPS_PARTIAL_ORDER(E): defines all relational operators to reflect the partial order induced by subseteq (see below)

Additionally, there exist the following Compare structs and objects:

  • totally ordered: struct boost::flags::total_order_t and object boost::flags::total_order

  • partially ordered: struct boost::flags::partial_order_t and object boost::flags::partial_order

As C++ allows to specialize std::less for user-defined types, we can provide a macro

  • BOOST_FLAGS_SPECIALIZE_STD_LESS(E): specializes std::less to use boost::flags::total_order

which ensures always total ordering for std::less based compare in containers and algorithms.

But for range-algorithms the language strikes back again: range compare uses std::ranges::less which is a struct and thus cannot be specialized. Internally it will do some checks and call operator < if available.
This means, when we define BOOST_FLAGS_REL_OPS_PARTIAL_ORDER(E) then for any range algorithm that compares values of type E we must provide boost::flags::total_order (or another total ordering) as Compare. Otherwise, we’re in UB land.

So, our recommendation (and the currently most comfortable way without loosing any safety) would be defining both BOOST_FLAGS_REL_OPS_DELETE(E) and BOOST_FLAGS_SPECIALIZE_STD_LESS(E).

Note

For the mathematically inclined programmer:
The function subseteq reflects the partial order (in the mathematical sense) of the generated Boolean algebra. You can enable relational operators using the BOOST_FLAGS_REL_OPS_PARTIAL_ORDER macro at global namespace.
BOOST_FLAGS_REL_OPS_PARTIAL_ORDER(E) defines the following semantics for the relational operators

  • e1 <= e2 : equivalent to subseteq(e1, e2)

  • e1 >= e2 : equivalent to subseteq(e2, e1)

  • e1 < e2 : equivalent to subset(e1, e2)

  • e1 > e2 : equivalent to subset(e2, e1)

  • e1 <=> e2 : has type std::partial_ordering and is equivalent to

    e1 == e2
    ? std::partial_ordering::equivalent
    : subseteq(e1, e2)
    ? std::partial_ordering::less
    : subseteq(e2, e1)
    ? std::partial_ordering::greater
    : std::partial_ordering::unordered

Flags and Flags

Instead of disjoint and intersect we can write:

  • !(e1 & e2) : equivalent to disjoint(e1, e2)

  • !!(e1 & e2) : equivalent to intersect(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 options logical_and, which enables operator&&, see next section.

Note

BOOST_FLAGS_AND is a macro defined as & boost::flags::pseudo_and_op_tag{} &.
The first & stores its left-hand argument in an intermediate type, which the second & evaluates with its right-hand argument.

Logically and and or

The logical operator && and operator || have to handled with care, as they are the only ones to support short-circuit evaluation for the built-in versions.

Let’s first take a look at the semantics of the built-in operator &&:

  • it converts its first arguments to bool, if its false then returns false (short-circuit evaluation)

  • otherwise converts the second arguments to bool and returns it

This is quite different from the semantics of the built-in bitwise & operator in a Boolean context:

  • it performs intergral promotion on both arguments and compute their bitwise AND

  • returns false if the outcome is equal to 0 otherwise true

E.g. in Boolean contexts (1 && 2) evaluates to true, while (1 & 2) evaluates to false.

Though, for operator || and operator | and values v1 and v2, in Boolean contexts (v1 || v2) and (v1 | v2) always evaluate to the same value, there is still the difference, that operator || uses short-circuit evaluation and operator | does not.

Furthermore, operator && and operator || have lower precedences than operator & and operator | (cf. C++ Operator Precedence), which is another source for errors.

Since logical operators && and || can easily get confused with their bitwise counterparts & and |, by default Boost.Flags disables operator&& and operator||, when both arguments are implicitly convertible to integral types, to prevent accidentally calling the built-in versions.

Nevertheless, sometimes operator && comes in handy or is even required, e.g. when Boost.Flags is used together with other generic libraries. When for an enabled enumeration E

BOOST_FLAGS_ENABLE_LOGICAL_AND(E)

// resp. for a class local enum
BOOST_FLAGS_ENABLE_LOCAL_LOGICAL_AND(E)

// instead of
// BOOST_FLAGS_ENABLE(E)
// resp.
// BOOST_FLAGS_ENABLE_LOCAL(E)

is defined, then operator && is overloaded for E with the semantics

    operator&&(e1, e2) -> bool { return !!(e1 & e2); }

And once more, there is no short-circuit evaluation!

Warning

When applying Boost.Flags to existing code, please be cautious enabling operator&&

  • it evaluates differently than the built-in version and doesn’t provide short-circuit evaluation

  • the usage of operator&& may have been wrong and operator& was intended

So, before enabling operator&&, it is best to compile the code without it and check whether the uses of operator&& are correct.

Note

To achieve short-circuit evaluation for expresions with flags, convert the flag-value arguments to bool by using e.g. !!(…​) && !!(…​) or any(…​) && any(…​).

Utilities

Functions

Boost.Flags provides the following functions for an enabled enumeration E:

  • make_null(E) -> E { return E{}; }

  • make_if(E e, bool set) -> E { return set ? e : E{}; }

  • modify(E e, E mod, bool set) -> E { return set ? e | mod : e & ~mod; }

  • modify_inplace(E& e, E mod, bool set) -> E& { e = modify(e, mod, set); return e; }

  • add_if(E e, E mod, bool add) -> E { return add ? e | mod : e; }

  • add_if_inplace(E& e, E mod, bool add) -> E& { e = add_if(e, mod, add); return e; }

  • remove_if(E e, E mod, bool remove) -> E { return remove ? e & ~mod : e; }

  • remove_if_inplace(E& e, E mod, bool remove) -> E& { e = remove_if(e, mod, remove); return e; }

  • get_underlying(E e) -> typename underlying_type<E>::type { return static_cast<typename underlying_type<E>::type>(e); }

  • from_underlying(typename underlying_type<E>::type u) -> E { return static_cast<E>(u); }

All utility functions are defined in namespace boost::flags.
The macro BOOST_FLAGS_USING_UTILITIES() imports all utility functions, except from_underlying, into the current naespace.

and for generating the values of enumerators:

  • template<typename T = int> nth_bit(unsigned int n) -> underlying_or_identity<T>::type { return static_cast<underlying_or_identity<T>::type>(1) << n; }
    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

  • template<typename T> next_bit(T v) -> T { return v << 1; }

Flags generator

Boost.Flags provides a flag_generator class template to iterate over the bits of a flag-enum. The class template is usually not create directly but through a call the one of the following functions

    // generates from first to last (incl.)
    constexpr flag_generator<E> flags_from_to(E first, E last);

    // generates from E(1) to last (incl.)
    constexpr flag_generator<E> flags_to(E last);

    // generates all bits of the underlying type of E, starting from E(1)
    constexpr flag_generator<E> flags_all();

Example:

// using enum pizza_toppings from example above
auto base_toppings = pizza_toppings::tomato | pizza_toppings::cheese;
for (auto f : boost::flags::bits_to(pizza_toppings::garlic)) {
    if (boost::flags::any(f & base_toppings)) {
        std::cout << get_underlying(f) << "\n";
    }
}

The template<typename E> flag_generator provides an internal iterator and member functions begin() and end() returning the resp. iterators.
It is contructed with the lowest and highest flag that shall be iterated over. If flag_generator is constructor with flags not having exactly one bit set, then the behaviour is undefined.

Other flags-like enum implementations

There are several articles and libraries on flag-like enums available. The following is not exhaustive list found on the web in January 2024:

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 (*)

+

-

-

+

+

-

+

+

library available

+

+

-

+

+

+

+

+

(*) 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 or their complements from the same enumeration

    • flag values must be distinguished 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

    • no new types for flags, except complement

  • opt-in shall be done by

    • function overload or

    • template specialization

  • 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

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.

Reference

The contents of the library are in namespace boost::flags.

All operators and functions in this library

  • are constexpr and noexcept

  • use attribute [[nodiscard]] except for assignment operators &=, |=, ^= and functions modify_inplace, add_inplace, remove_inplace

Most of the function signatures for operators are written with trailing return type but the leading auto is left out for readability.

Supported language versions

Boost.Flags requires at least C++11.

Concepts are used if available and not disabled using BOOST_FLAGS_HAS_CONCEPTS.

Macros to opt-in

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_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_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_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_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_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_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_ENABLE…​ macros.

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.

Note

To import the utility functions into the current namespace the macro BOOST_FLAGS_USING_UTILITIES() must be used explicitly.
There is no BOOST_FLAGS_ENABLE…​ macro that entails BOOST_FLAGS_USING_UTILITIES().

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. Instead of BOOST_FLAGS_ENABLE(E), the sequence

constexpr bool boost_flags_enable(E) { return true; }
BOOST_FLAGS_FORWARD_OPERATORS(E)

can be used, which creates forwarding functions instead of importing the operators.

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 using declarations at class scope, Boost.Flags uses BOOST_FLAGS_FORWARD_OPERATORS_LOCAL to ensure that all operators can be found through ADL.

boost_flags_enable

The function boost_flags_enable can be overloaded for an enumeration E either

  • with return-type bool:

    constexpr bool boost_flags_enable(E) { return true; }

    enables Boost.Flags for an enumeration E. All optional features are disabled.

  • or with return-type boost::flags::options: The flag-enumeration boost::flags::options has the options

    • enable: enables E

    • disable_complement: disables the usage of complement<E> (see also disable_complement)

    • logical_and: enables operator&& for E (see also logical_and) e.g.

      constexpr boost::flags::options boost_flags_enable(E) {
          return 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&&
              ;
      }

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; }
    // ...
};

A Boost.Flags enabled (scoped/unscoped) enumeration will also be called (scoped/unscoped) flags.

Alternatively to using boost_flags_enable, the class template boost::flags::enable can be specialized.

enable<T>

To enable the Boost.Flags functionality specialize the 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 {};

A Boost.Flags enabled (scoped/unscoped) enumeration will also be called (scoped/unscoped) flags.

When boost::flags::enable is specialized for E any definition of boost_flags_enable for E are ignored (cf. boost_flags_enable).

disable_complement

If the specialization of 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 behaviour. For more information, see the following note.

Example:

template<> struct boost::flags::enable<E>
    : std::true_type
    , boost::flags::disable_complement
    {};
Note

For unscoped enumerations with unspecified underlying type, the compiler infers two types:

  • the underlying type (https://eel.is/c++draft/dcl.enum#7), which we can query using std::underlying_type

  • a hypothetical integer value type with minimal width such that all enumerators can be represented (https://eel.is/c++draft/dcl.enum#8)
    "The width of the smallest bit-field large enough to hold all the values of the enumeration type […​]."

The legal values for the enumeration are those of the hypothetical integer value type.

But, this hypothetical integer value type is not deducible from the type of the enumeration. (It requires to know the values of all enumerators.) Furthermore, it is in general not the same as the underlying type. E.g.

enum TriBool {
    false_ = 0,
    true_ = 1,
    undefined = 2
};

has underlying type int on all major compilers, but its valid values are just 0, 1, 2 and 3 as the hypothetical integer value type with minimal width is a 2-bit unsigned integer.

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
    {};

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 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. (cf. BOOST_FLAGS_SPECIALIZE_STD_LESS).

There is also a functions object

static constexpr boost::flags::total_order_t total_order;

partial_order_t

The type boost::flags::partial_order_t defines a call-operator which accepts two compatible flag-arguments and compares them based on flag entailment. (cf. BOOST_FLAGS_REL_OPS_PARTIAL_ORDER).

There is also a functions object

static constexpr boost::flags::partial_order_t partial_order;

Operators

operator~

Reverses all bits of the underlying integer representation of its argument.

The signature of operator~ depends on whether complement is enabled (cf. 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. 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 operator& where at least one of the arguments is enabled and both are implicitly convertible to an integer type are deleted.

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. 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 operator| where at least one of the arguments is enabled 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. 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 operator^ where at least one of the arguments is enabled 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. 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 operator&=(complement<E>&, E) is not available, as it would change the type of the first argument.

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. 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 operator|=(E&, complement<E>) is not available, as it would change the type of the first argument.

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. disable_complement).

When complement is enabled for flags E

  • operator^=(E&,E) -> E&

  • operator^=(complement<E>&,E) -> complement<E>&

Note

The assignment operator^=(E&, complement<E>) and operator^=(complement<E>&, complement<E>) are not available, as they would change the type of the first argument.

otherwise

  • operator^=(E&,E) -> E&

operator!

Tests if a value is empty.

    // pseudo code
    [[nodiscard]] constexpr bool operator!(E e) noexcept { return e == E{}; }

Pseudo operator BOOST_FLAGS_AND

Takes the bitwise AND of its arguments and converts the result to bool.

    // pseudo code
    [[nodiscard]] constexpr bool operator BOOST_FLAGS_AND(E e1, E e2) noexcept { 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 only given for operator==. Calls with reversed arguments and to operator!= will constructed by rewrite rules.

  • 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 operator== and operator!= where at least one of the arguments is enabled and both are implicitly convertible to an integer type are deleted.

Note

If E is a scoped enumeration then

    bool foo(E e){
        return e == 0;  // literal 0 converts to nullptr, thus
                        // operator==(E, std::nullptr_t) will be called
    }

compiles and returns the expected result.

when E is unscoped then above code will fail to compile: the overload operator(E, int) is deleted for unscoped enumerations.
If it wasn’t then comparison with arbitrary integer values would be possible, as unscoped enumerations implicitly convert to their underlying integer type.

Relational operators <, <=, > and >=

The relational operators for enumerations (scoped and unscoped) are provided by the language (by applying the resp. operator to the underlying value). Furthermore, the current wording doesn’t allow overloading or deleting those operators by a templated operator (there is a pending Defect Report on this topic: https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2730).

There are macros BOOST_FLAGS_REL_OPS_PARTIAL_ORDER to overload and BOOST_FLAGS_REL_OPS_DELETE to delete relational operators.

Warning

It is recommended to define either BOOST_FLAGS_REL_OPS_DELETE for flag-like enumerations.

This prohibits the accidental usage of relational operators with flag values ( The built-in semantics for relational operators compare the underlying numerical values and do not coincide with flag entailment!).

When flags have to be stored in ordered container or sorted, please either define BOOST_FLAGS_SPECIALIZE_STD_LESS(E) for the enumeration or specify boost::flags::total_order_t as Compare predicate type.

Range algorithms require the specification of boost::flags::total_order as compare object.

Boolean predicates

any

Tests if a value is not empty.

    // pseudo code
    [[nodiscard]] constexpr bool any(E e) noexcept { return e != E{}; }

none

Tests if a value is empty.

    // pseudo code
    [[nodiscard]] constexpr bool none(E e) noexcept { return e == E{}; }

intersect

Tests if two values have common bits set.

    // pseudo code
    [[nodiscard]] constexpr bool intersect(E e1, E e2) noexcept { return e1 & e2 != E{}; }

disjoint

Tests if two values do not have a common bit set.

    // pseudo code
    [[nodiscard]] constexpr bool disjoint(E e1, E e2) noexcept { return e1 & e2 == E{}; }

subseteq

Tests if all bits set in the first argument are also set in the second argument.

    // pseudo code
    [[nodiscard]] constexpr bool subseteq(E e1, E e2) noexcept { return e1 & e2 == e1; }

subset

Tests if the bits set in the first argument are a proper subset of the bits in the second argument.

    // pseudo code
    [[nodiscard]] constexpr bool subset(E e1, E e2) noexcept { return subseteq(e1, e2) && (e1 != e2); }

Utility functions

make_null

    // pseudo code
    [[nodiscard]] constexpr E make_null(E) noexcept { return E{}; }

For flags E returns an empty instance of type E, i.e. with underlying value of 0.

make_if

    // pseudo code
    [[nodiscard]] constexpr E make_if(E e, bool set) noexcept { return set ? e : E{}; }

Depending on set returns either the first argument or empty an instance of type E.

modify

    // pseudo code
    [[nodiscard]] constexpr E modify(E e, E mod, bool set) noexcept { 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
    constexpr E& modify(E& e, E mod, bool set) noexcept { e = modify(e, mod, set); return e; }

Similar to modify but applies the modification to e and returns it as reference.

add_if

    // pseudo code
    [[nodiscard]] constexpr E add_if(E e, E mod, bool add) noexcept { 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
    constexpr E& add_if(E& e, E mod, bool add) noexcept { 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
    [[nodiscard]] constexpr E remove_if(E e, E mod, bool remove) noexcept { 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
    constexpr E& remove_if(E& e, E mod, bool remove) noexcept { e = remove_if(e, mod, remove); return e; }

Similar to remove_if 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
    constexpr U get_underlying(E e) noexcept { 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
    constexpr E from_underlying(U u) noexcept { return static_cast<E>(u); }

nth_bit

    // pseudo code
    template<typename T = int>
    [[nodiscard]] constexpr underlying_or_identity<T>::type nth_bit(unsigned int n) noexcept {
        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>
    [[nodiscard]] constexpr 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 behaviour is undefined if more than one bit is set in value v.

Flags generator

flag_generator

The template<typename E> flag_generator provides an internal iterator and member functions begin() and end() returning the resp. iterators.
It is contructed with the lowest and highest flag that shall be iterated over. If flag_generator is constructor with flags not having exactly one bit set, then the behaviour is undefined.

flags_from_to

The function

    template<typename E>
    [[nodiscard]] constexpr flag_generator<E> flags_from_to(E first, E last);

returns a flag_generator instance iterating from first to last (incl.).

flags_to

The function

    template<typename E>
    [[nodiscard]] constexpr flag_generator<E> flags_to(E last);

returns a flag_generator instance iterating from E(1) to last (incl.).

flags_all

The function

    template<typename E>
    [[nodiscard]] constexpr flag_generator<E> flags_all();

returns a flag_generator instance iterating from E(1) to the most significant bit of the underlying type of E (incl.), i.e. all bits of the underlying type.

Macros

BOOST_FLAGS_NULL

The macro BOOST_FLAGS_NULL can be used for (in-)equailty 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_REL_OPS_DELETE

The macro BOOST_FLAGS_REL_OPS_DELETE(E) deletes all relational operators for a Boost.Flags enabled enumeration E.

The macro BOOST_FLAGS_REL_OPS_DELETE(E) has to 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).

The macro BOOST_FLAGS_SPECIALIZE_STD_LESS(E) has to be defined at global namespace.

The definition of BOOST_FLAGS_SPECIALIZE_STD_LESS(E) will not enablerange algorithms to use that total order. Here it is required to explicitely specify boost::flags::total_order as compare object.

BOOST_FLAGS_REL_OPS_PARTIAL_ORDER

The macro BOOST_FLAGS_REL_OPS_PARTIAL_ORDER(E) defines all relational operators for a Boost.Flags enabled enumeration E.
The following semantics apply

  • e1 <= e2 : equivalent to contained(e1, e2)

  • e1 >= e2 : equivalent to contained(e2, e1)

  • e1 < e2 : equivalent to (contained(e1, e2) && e1 != e2)

  • e1 > e2 : equivalent to (contained(e2, e1) && e1 != e2)

  • e1 <=> e2 : has type std::partial_ordering and is equivalent to

    e1 == e2
    ? std::partial_ordering::equivalent
    : contained(e1, e2)
    ? std::partial_ordering::less
    : contained(e2, e1)
    ? std::partial_ordering::greater
    : std::partial_ordering::unordered

The macro BOOST_FLAGS_REL_OPS_PARTIAL_ORDER(E) has to be defined at global namespace.

BOOST_FLAGS_SPECIALIZE_STD_LESS

The macro BOOST_FLAGS_SPECIALIZE_STD_LESS(E) specializes std::less for E to use boost::flags::total_order.

The macro BOOST_FLAGS_SPECIALIZE_STD_LESS(E) has to be defined at global namespace.

Configuration Macros

If not specified, all the following macros are deduced from the system, compiler, C++ version. (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_INLINE_VARIABLES

Specifies, if the inline variables are available (and work correctly: broken in msvc before version v142).

Possible values: 0 or 1

BOOST_FLAGS_DEFINE_PARTIAL_ORDERING_OBJECTS

Specifies, if the partial_ordering emulation is defined.
Only used, when std::partial_ordering is not available.

Possible values: 0 or 1

BOOST_FLAGS_HAS_REWRITTEN_CANDIDATES

Specifies, if rewitten candidates are availabe.

Possible values: 0 or 1

BOOST_FLAGS_WEAK_SYMBOL

Text used to specify a symbol with weak linkage

  • __declspec(selectany) for mscv and clang-cl, ICC on windows

  • __attribute__((weak)) for g and clang, except for g on mingw: in that case please define BOOST_FLAGS_DEFINE_PARTIAL_ORDERING_OBJECTS=1 in exactly one translation unit

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]]).

This documentation is