如何在C ++中避免使用带有链式选项的嵌套if语句?
例如,如果类型A包含std::optional<B> b
而类型B包含std::optional<C> c
,我希望能够编写如下内容:
const auto v = if_exists(if_exists(a->b)->c);
如果b或c为空选项,则v将从c或空选项中获取值。
我认为嵌套if会更好:
if (a->b) {
const auto b = *(a->b);
if (b->c) {
const auto c = *(b->c);
}
}
以下问题似乎朝这个方向发展,但我不确定如何使其适应我的用例:Haskell style "Maybe" type & *chaining* in C++11
答案 0 :(得分:2)
这可以通过一个简单的宏来实现。
#define CHAIN(OPTIONAL, MEMBER) \
([](auto &&opt) { \
return opt ? std::optional{opt->MEMBER} : std::nullopt; \
}(OPTIONAL))
const auto v = CHAIN(CHAIN(a, b), c);
答案 1 :(得分:0)
使用引用问题中给出的代码,您可以执行类似这样的操作
maybe_do(a->b, [](B &b){
return maybe_do(b.c, [](C &c){
//do what You want to do with the C optional
})
});
答案 2 :(得分:0)
您可以执行以下操作(伪代码ish;下面提供了可构建代码的链接):
// wrap std::optional for chaining
template <class T> class Maybe {
std::optional<T> t;
// ... constructors etc
// Maybe chaining
// If A has a member named m of type M,
// then Maybe<A>.fetch(&A::m) returns a Maybe<M>
template <class M>
Maybe<M> fetch(M T::*mem_ptr) {
return (bool(t)) ? Maybe<M>((*t).*mem_ptr) : Maybe<M>() ;
}
// Maybe chaining special case
// If A has a member named m, which is itself a Maybe<M>,
// then return it without wrapping it in an additional Maybe
template <class M>
Maybe<M> fetch(Maybe<M> T::*mem_ptr) {
return (bool(t)) ? ((*t).*mem_ptr) : Maybe<M>() ;
}
};
现在,如果你有这个:
struct C { int d ; }
struct B { C c; }
struct A { B b; }
A a;
Maybe<A> ma;
你可以这样做
int d = a.b.c.d;
你不能对ma
做同样的事,但你可以使用下一个最好的东西,即:
Maybe<int> md = ma.fetch(&A::b).fetch(&B::c).fetch(&C::d);
如果您Maybe
- 依据上述任何或所有struct
成员,您仍然可以使用此功能:
struct C { Maybe<int> d ; }
struct B { Maybe<C> c; }
struct A { Maybe<B> b; }
答案 3 :(得分:0)
您可以使用
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);