C++: std::monostate

std::monostate was released with std::variant in C++17.

Essentially, std::monostate is a type where all its instances have exactly the same (and empty) state, hence its name.

Think of its implementation as being similar to something like this:

class empty { };

And what is it useful for?

  1. An instance of this class can be used to represent the initial value of a std::variant when declared as its first type. A std::variant MUST always contain a valid value, so std::monostate is quite useful when its initial value is not known a priori or when the other data types do not have default constructors.
std::variant<std::monostate, int, std::string> value; // here value is instantiated using std::monostate.
  1. It can be used when representing a “no-value” as a valid std::variant value is needed.
std::variant<int, std::string, std::monostate> value{2}; // stores an int.
value = std::monostate{}; // now stores a new instance of std::monostate.
  1. Independently from std::variant, it can be used when a class with no data is required. A concrete example?

Currently, I am working on a UI layout system. The layout_container can store UI widgets and, depending on the layout policies used, they need to specify a “constraint.” The constraint could be a relative XY coordinate, or a position inside a border_layout (north, south, etc.) or NOTHING (for example, in a flow-based layout, the UI controls are positioned one next to the other, flowing like text, and thus, they do not need any constraint).

So I could define my layout_container like this:

template <typename Constraint, typename LayoutPolicy>
class layout_container;

and have the specific types like:

using border_layout_container = layout_container<border_layout_constraint, border_layout_policy>;
using xy_layout_container = layout_container<xy_layout_constraint, xy_layout_policy>;
using flow_layout_container = layout_container<std::monostate, flow_layout_policy>;

I do not need to change anything in my layout_container to support (or not) constraints thanks to std::monostate.

  1. Additionally, std::monostate implements all comparison operators (operator==, operator!=, etc.). All instances are equal, by the way. Finally, std::monostate can also be used as a key on std::unordered_map instances because it provides a specialization for std::hash.

Want to read more about std::variant? Check this out!