Target-typing in modern C++

Solution 1:

In C++, expressions have types.

{} is not, however, an expression. There are a handful of restricted contexts where {} could be used when you'd expect an expression should go there. This doesn't make {} into an expression.

?: is an operation, and its arguments must have types. The type of ?: is derived from the type of its 2 arguments (the rules are complex, but it tries to find a common type).

b ? std::optional{5} : std::nullopt;

is similar to what you want. Here, the fact that std::nullopt can convert to a std::optional<int> is used to deduce the type of ?: expression.

You can also write:

template<class T>
std::optional<std::decay_t<T>> maybe( bool b, T&& t ) {
  if (!b) return std::nullopt;
  return std::forward<T>(t);
}

which lets you write

maybe( b, 5 )

for your case.

Fancier things can be done, where you use template<class T> operator T() on a type to deduce the type you are returning into.

But, in general, the C++ type system goes one way.