Good start, but I think you’re over-engineering in your zeal to make your class foolproof. Personally I’d recommend ‘worse is better’. First, let’s reuse Boost.Optional:
struct nothing_type {
template<typename T>
operator boost::optional<T>() const
{ return {}; }
};
constexpr nothing_type nothing;
template<typename T>
boost::optional<T>
just(T&& t)
{
return std::forward<T>(t);
}
template<typename Option, typename Functor>
auto maybe_do(Option&& option, Functor&& functor)
-> boost::optional<
decltype( functor(*std::forward<Option>(option)) )
>
{
// Forwarding
if(option)
return functor(*std::forward<Option>(option));
else
return nothing;
}
Some various explanations on things that aren’t really important:
-
nothingdoesn’t have to be an object, it can still be a function (returningnothing_type) like you’re doing. That’s not important. -
I made sure to preserve the reference semantics of
justto match your version. As a bonus though, it can still deal with values. As such, withint i = 0; auto maybe = just(i);then the type ofmaybewill beboost::optional<int&>, whereas withauto maybe = just(42);it isboost::optional<int>. -
the
*std::forward<Option>(option)can actually simply be*optionas Boost.Optional is not move-aware and not many compilers support lvalue/rvalue*this(which would be needed for it to matter). I just like future-proofing perfect-forwarding templates. -
you can still name
maybe_dooperator|instead. I would however recommend putting it in a namespace and useusing ns::operator|(orusing namespace ns;) to put it into scope. You can additionally (or instead) add an SFINAE check (or write several overloads) to make sure it only participates in overload resolution at appropriate times. I’m advising this to avoid namespace pollution and annoying errors.
The important stuff:
It may look like maybe_do is severely underpowered compared to your overloads that can deal with member pointers. But I’d recommend keeping it simple and instead putting the burden on client-code to adapt member pointers:
auto maybe = /* fetch an optional<T cv ref> from somewhere */
maybe_do(maybe, std::bind(&T::some_member, _1));
Similarly client code can use std::bind to do the poor man’s partial evaluation:
maybe_do(maybe, std::bind(some_functor, _1, "foo", _2, bar));