You might use
template <typename T, typename F>
auto convert_optional(const std::optional<T>& o, F&& f)
-> std::optional<std::decay_t<decltype(std::invoke(std::forward<F>(f), *o))>>
{
if (o)
return std::invoke(std::forward<F>(f), *o);
else
return std::nullopt;
}
template <typename T, typename F>
auto convert_optional(std::optional<T>& o, F&& f)
-> std::optional<std::decay_t<decltype(std::invoke(std::forward<F>(f), *o))>>
{
if (o)
return std::invoke(std::forward<F>(f), *o);
else
return std::nullopt;
}
template <typename T, typename F>
auto convert_optional(std::optional<T>&& o, F&& f)
-> std::optional<std::decay_t<decltype(std::invoke(std::forward<F>(f), *std::move(o)))>>
{
if (o)
return std::invoke(std::forward<F>(f), *std::move(o));
else
return std::nullopt;
}
or
template <typename> struct is_optional : std::false_type {};
template <typename T> struct is_optional<std::optional<T>> : std::true_type {};
template <typename O, typename F>
auto convert_optional(O&& o, F&& f)
-> std::enable_if_t<
is_optional<std::decay_t<O>>::value,
std::optional<std::decay_t<decltype(std::invoke(std::forward<F>(f),
*std::forward<O>(o)))>>>
{
if (o)
return std::invoke(std::forward<F>(f), *o);
else
return std::nullopt;
}
and your example becomes:
auto c = convert_optional(convert_optional(a, &A::b).value_or(std::nullopt),
&B::c).value_or(std::nullopt);
convert_optional(a, &A::b)
will return std::optional<std::optional<B>>
You might even simplify by additional function:
template <typename O, typename F>
auto convert_optional_fact(O&& o, F&& f)
-> decltype(convert_optional(std::forward<O>(o),
std::forward<F>(f)).value_or(std::nullopt))
{
return convert_optional(std::forward<O>(o),
std::forward<F>(f)).value_or(std::nullopt);
}
and then
auto c = convert_optional_fact(convert_optional_fact(a, &A::b), &B::c);
Demo
std::optional
andc++11
doesn't sound right to me. – Ringleaderif (a->b && a->b->c) { const auto c = *a->b->c; // do stuff with c }
– Quintan