如何避免在 C++ 中使用链式选项嵌套 if 语句?
例如,如果类型 A 包含 std::optional<B> b
和 B 型 std::optional<C> c
,我希望能够写出类似的东西:
const auto v = if_exists(if_exists(a->b)->c);
如果 b 或 c 是空 optional 项,则 v 将从 c 或空 optional 项中获取值。
我认为这样嵌套 ifs 会更好:
if (a->b) {
const auto b = *(a->b);
if (b->c) {
const auto c = *(b->c);
}
}
以下问题似乎朝这个方向发展,但我不确定如何使其适应我的用例:Haskell style "Maybe" type & *chaining* in C++11
最佳答案
你可能会用
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;
}
或者
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;
}
你的例子变成:
auto c = convert_optional(convert_optional(a, &A::b).value_or(std::nullopt),
&B::c).value_or(std::nullopt);
convert_optional(a, &A::b)
将返回 std::optional<std::optional<B>>
您甚至可以通过附加功能来简化:
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);
}
进而
auto c = convert_optional_fact(convert_optional_fact(a, &A::b), &B::c);
Demo
关于c++ - C++ 中的链 optional ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48905531/