在Modern C ++中,有没有一种方法可以进行安全导航?
例如,不要做...
if (p && p->q && p->q->r)
p->q->r->DoSomething();
Run Code Online (Sandbox Code Playgroud)
...通过使用某种短路的智能指针,或利用运算符重载的某种其他语法,或在标准C ++库或Boost中的某些东西,具有简洁的语法。
p?->q?->r?->DoSomething(); // C++ pseudo-code.
Run Code Online (Sandbox Code Playgroud)
上下文尤其是C ++ 17。
我们可以做到这一点:
p >> q >> r >> doSomething();
这是样板...
#include <iostream>
struct R {
void doSomething()
{
std::cout << "something\n";
}
};
struct Q {
R* r;
};
struct P {
Q* q;
};
struct get_r {};
constexpr auto r = get_r{};
struct get_q {};
constexpr auto q = get_q{};
struct do_something {
constexpr auto operator()() const {
return *this;
}
};
constexpr auto doSomething = do_something {};
auto operator >> (P* p, get_q) -> Q* {
if (p) return p->q;
else return nullptr;
}
auto operator >> (Q* q, get_r) -> R* {
if (q) return q->r;
else return nullptr;
}
auto operator >> (R* r, do_something)
{
if (r) r->doSomething();
}
void foo(P* p)
{
//if (p && p->q && p->q->r)
// p->q->r->DoSomething();
p >> q >> r >> doSomething();
}
Run Code Online (Sandbox Code Playgroud)
最终的组装结果是非常可以接受的。走到这一步的旅程可能不会......
foo(P*):
test rdi, rdi
je .L21
mov rax, QWORD PTR [rdi]
test rax, rax
je .L21
cmp QWORD PTR [rax], 0
je .L21
mov edx, 10
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:std::cout
jmp std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
.L21:
ret
Run Code Online (Sandbox Code Playgroud)
您能做的最好的事情就是将所有成员访问权折叠到一个函数中。假定不检查所有内容是否都是指针:
template <class C, class PM, class... PMs>
auto access(C* c, PM pm, PMs... pms) {
if constexpr(sizeof...(pms) == 0) {
return c ? std::invoke(pm, c) : nullptr;
} else {
return c ? access(std::invoke(pm, c), pms...) : nullptr;
}
}
Run Code Online (Sandbox Code Playgroud)
可以让您编写:
if (auto r = access(p, &P::q, &Q::r); r) {
r->doSomething();
}
Run Code Online (Sandbox Code Playgroud)
没关系。或者,您可能会因运算符重载而有些疯狂,并产生类似以下内容的内容:
template <class T>
struct wrap {
wrap(T* t) : t(t) { }
T* t;
template <class PM>
auto operator->*(PM pm) {
return ::wrap{t ? std::invoke(pm, t) : nullptr};
}
explicit operator bool() const { return t; }
T* operator->() { return t; }
};
Run Code Online (Sandbox Code Playgroud)
它使您可以编写:
if (auto r = wrap{p}->*&P::q->*&Q::r; r) {
r->doSomething();
}
Run Code Online (Sandbox Code Playgroud)
没关系 不幸的是,没有->?或没有.?类似的运营商,所以我们有点不得不工作。