Casting workaround

The templated Impl type on each policy function is decltype(*this) of the part of Outcome’s internal implementation which calls the policy. It therefore may be quite removed from the actual outcome type in play, and thus the .exception() member function and others belonging to outcome only will not be immediately visible.

Hence a very common thing you’ll need to do is static cast to a more derived form before use like this:


template <class T, class EC, class E> struct outcome_policy
{
  /*! Performs a wide check of state, used in the value() functions.

  \effects If outcome does not have a value, if it has an exception it rethrows it via `std::rethrow_exception()`,
  if has an error it throws a `std::system_error(error())`, else it throws `bad_outcome_access`.
  */
  template <class Impl> static constexpr void wide_value_check(const Impl &self)
  {
    // All of the have_*() state check functions are always present in all Impl types
    if(!self.have_value())
    {
      if(self.have_exception())
      {
        // .value() is implemented by the result part of Outcome. It knows nothing
        // of outcome's .exception(), so we need to cast to the derived type first.
        const MyOutcomeType &_self = static_cast<const MyOutcomeType &>(self);

        // Note this will invoke narrow_exception_check() in this policy
        std::rethrow_exception(_self.assume_exception());
      }
      if(self.have_error())
      {
        throw std::system_error(make_error_code(self.assume_error()));
      }
      throw bad_outcome_access("no value");
    }
  }
};
View this code on Github

Note that const-ness and lvalue/rvalue-ness is propagated to Impl &&self, so if the result or outcome is a const rvalue and the user calls .value() on that, the wide_value_check() will see a const rvalue self. This lets you move from self when implementing the wide value check appropriately.