5

I suspect boost::optional's get_value_or was deprecated because it is unsafe if an rvalue is passed as the default parameter. However, it is occasionally useful to be able to reference the optional value or a default alternative.

Is the following safe?

template<typename T>
T const& get_reference_or(boost::optional<T> const& opt, T const& alt)
{
    if (opt) return opt.get();
    else return alt;
}

template<typename T>
T const& get_reference_or(boost::optional<T> const&, T&&) = delete;
Rai
  • 1,328
  • 11
  • 20

1 Answers1

3

As written, your code has problems with type deduction, because T is deducible from both arguments. But suppose you actually made T only deducible from the optional:

template <class T>
struct NonDeducedHelper { using type = T; };

template <class T>
using NonDeduced = typename NonDeducedHelper<T>::type;

template<typename T>
T const& get_reference_or(boost::optional<T> const& opt, NonDeduced<T> const& alt)
{
    if (opt) return opt.get();
    else return alt;
}

template<typename T>
T const& get_reference_or(boost::optional<T> const&, NonDeduced<T>&&) = delete;

Then, the code is almost safe, since when a non-const rvalue is used as the default for get_reference_or, it will attempt to use the deleted overload and fail to compile. However, to be 100% safe, you should also delete the overload for const rvalues:

template<typename T>
T const& get_reference_or(boost::optional<T> const&, NonDeduced<T> const&&) = delete;
palotasb
  • 4,108
  • 3
  • 24
  • 32
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455