The Merits of optional

The Meritsof optional<T const&>

Passing function parameters by const& is very common:

void foo(T const& t);

Now, what do we do if such a parameter is optional?

std::optional<T> does not allow T to be a reference. We could use

void foo(std::optional<T> const& ot);

but this defies the purpose of passing by const&: if we want to pass a T, we would need to first copy it into an std::optional<T>, but we pass by const& precisely to avoid copying.

C++ implements references as pointers, and pointers, unlike references, can be null. They also have the same syntax as std::optional: contextual conversion to bool checks for null, and operator* accesses the value. Of course, pointers were there first, so really, std::optional got its syntax from pointers.

So why not use pointers?

void foo(T const* pt);

This compiles to the right code, but is somewhat inconvenient to use. Instead of

foo(t);

one must write

foo(&t);

or, if afraid of operator& being overloaded, even

foo(std::addressof(t));

If t is an rvalue, it gets even worse because C++ does not allow taking the address of an rvalue:

foo(std::addressof(static_cast<T&>(make_t())));

It does the right thing, but is not how we want to program.

Also, once inside foo, you can do operations on pointers that you should not be able to do:

void foo(T const* pt) {
… pt[1] … // this compiles :-(
}

So why doesn't the standard allow optional references?

void foo(std::optional<T const&> ot)

The reason is that the C++ committee could not agree on what would happen when assigning the contained type:

std::optional<T const&> ot(…);
T t=…;
ot = t; // what does this do?

Does this rebind ot to now point to t, or is this equivalent to *ot=t, changing the referenced value, under the precondition that ot is not std::nullopt?

However, for many use cases, rebinding isn't needed. Actual references also cannot be rebound. And assigning to the contained value can be achieved by *ot=t, which may be the clearer syntax anyway.

So we can postpone the discussion to another day by not supporting assignment to optional<T&> at all. optional<T&> is still very useful to pass and return optional reference parameters. The think-cell library allows references for its tc::optional. tc::optional differs from std::optional in another way, which we will talk about next time.

Share