Skip to content

Last updated:2026-10-12

C++17 optional_ref

This post retells foonathan's blog optional_ref discussion; the original is the single best discussion of why std::optional<T&> (an optional reference) never made it into the standard. The topic was dug up again right before C++26, so it is worth walking through the core arguments.

1. The Problem: Why Doesn't std::optional Compile?

std::optional<int>, std::optional<std::string> are both fine, but std::optional<int&> fails to compile outright. The reason is a bit of "historical baggage": when the standard was drawn up, no consensus was reached on its assignment semantics, so the standard simply states that behavior is undefined when T is a reference type.

So what is so hard about a "possibly empty reference"? The author distilled four questions:

(1) Is optional<T&> the same thing as a pointer?

(2) Do we really need optional<T&>?

(3) On assignment, should it rebind (rebind to the new object) or assign-through (write to the referred object)?

(4) Should it even have an assignment operator at all?

The author's tl;dr answers: no, we do not, rebind, and it should not. Let us see why.

2. An optional Reference Is Not a Pointer

Intuitively, a "possibly empty reference" is just a pointer, but the author points out a more essential difference:

  • creation of and access to a T& are both implicit: once you have the reference you just use it, no dereference needed;
  • creation of and access to a T* are both explicit: you need &x to take the address and *p to dereference.

This explicitness difference has value: a pointer reminds you "this may be null, there may be aliasing problems here". Making optional<T&> an "easier pointer" with implicit conversions everywhere throws away exactly this layer of protection. The author's conclusion: if you want to express "may be empty", an explicit T* is actually better documentation.

3. rebind or assign-through

This is the core deadlock of the whole discussion:

int a = 1, b = 2;
std::optional<int&> opt = a;
opt = b;  // 问题:这句是什么意思?

Two readings:

  • rebind: opt now binds to b (the alias semantics of references — int& ra = a; ra = b; assigns b to a... wait, assignment of int& is precisely assign-through!)
  • assign-through: write b's value into a (the semantics of an ordinary reference T&).

Here lies the contradiction: if an optional reference wants to align with the semantics of T&, assignment should be assign-through; yet a mutable optional (one that can be re-filled with another object) intuitively demands rebind. Both semantics are reasonable, the committee could not settle it, and so optional<T&> was never legalized.

In practice, when you want rebind semantics, the community's solution is to roll your own optional_ref<T>: internally it stores a T*, and assignment just swaps the pointer — semantics crystal clear. The C++26 std::optional_ref proposal follows the same route.

4. Summary

(1) optional<T&> is not syntactic sugar for a pointer; the two have an essential division of labor in explicitness;

(2) what blocked it is the ambiguity of assignment semantics: both rebind and assign-through make sense;

(3) when you need a "nullable reference", an explicit pointer or an optional_ref is a safer choice than a "legalized optional".

Finally, a question to ponder: should optional<T&>'s value() and *opt return T& or const T&? And if you also want to express captured-as-const semantics, how should the template parameter be designed?

Comments