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.