Nullable type in C++

std::optional is a type wrapper which wraps that type and an optional flag which says about the nullability of that type.

Sometimes we create an object which contains no valid data. We often a associate a flag or a special value, which decides the validity of the data. For example,

  • a pointer with value nullptr is a pointer that points to no location.
  • an id type with an allowed value of 0, which says the id is invalid if 0 or it’s a valid if more than 0.

The problem is we need to know what is that special value that represents absence of value. For a type representing ID of type int, 0 is the special value. For pointers, nullptr is a special value. Special value depends on the underlying type. This is cumbersome to maintain when the application has 100s of types.

A nullable value type can be represented as a wrapper of an existing type and a method which says whether the instance has a valid value. In C++17, std::optional was introduced to represent nullable value type. std::optional objects usually have the contained object and a boolean flag.

  • If it contains std::nullopt, it means it has no value. When it has no value, the value is in uninitialized state.
  • It supports both copy semantics and move semantics. The semantics depends on the underlying type.
  • It has a small memory overhead for the boolean flag plus alignment.
  • A few functions to access the state of the object and contained object.

The above functions can be written as follows using std::optional.

Instead of returning 0 as a special value, we return std::nullopt. In the calling site, we don’t have to worry about what is the special value.

Construction

We can create std::optional object which has no value at all. The contained object won’t be constructed at all.

Here, the constructor of contained object is not called. When the std::optional instance is created, a byte array of size and alignment enough to hold the object is allocated. When it needs to construct the contained object, then placement new is used to create the object at the same memory location. For one std::optional instance, there can be many contained objects in a lifetime, but memory is always fixed.

We can initialize with some value. Because of C++17’s type deduction rules, type does not need to be specified.

Here, an unnecessary temporary will be created. We can use std::in_place to construct the object in place and avoid temporary. In place construction will need the type to be explicitly specified.

std::optional has a constructor, which takes universal reference. This allows in place construction avoiding creation of unnecessary temporaries.

There is an utility function, std::make_optional, can also be used to do the in place construction with std::in_place tag.

In place construction is useful, if a type is neither copy-able nor movable.

std::optional can also be copy or move constructed from other std::optional objects.

Accessing the contained value

std::optional provides conversion to bool operator and has_value() method to check if it has a value.

std::optional provides * and -> overloaded operator to access the contained object. If it has no value, it will have an undefined behavior. It also provides value() method which throws std::bad_optional_access exception in case it has no value.

There is a method value_or(param..), which has a fallback mechanism if the optional object contains no value.

operator * and value() returns object by reference. So, you should avoid it using on temporaries.

value_or() returns by value. So, you should avoid it using on objects where copy is an expensive operation.

Modifying the value

std::optional provides emplace() method assign a new value to the contained type. emplace() takes a universal reference. So, if a temporary is given, it will move construct the contained object.

It also provides reset() to destroy the underlying object.

Comparison

  • If both optional objects have value, then the contained objects are compared. If the contained objects can’t be compared, the compiler will give error.
  • If both optional objects have no value, then they are equal.
  • A comparison between two objects where one has value and other has no value, the object with a value will always be greater.

Thanks for reading !!

C++11/14, Qt, Juce

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store