C ++是否有安全的导航运算符?

Elj*_*jay 4 c++ c++17

在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。

Ric*_*ges 6

“用一点样板文件......”

我们可以做到这一点:

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)

  • @Eljay 优化器为我们做了短路,所以这不是问题。您可能需要一个代码生成器来为整个项目执行此操作。您可以使用像“yacc”这样的工具快速构建它。https://en.wikipedia.org/wiki/Yacc (2认同)

Bar*_*rry 5

您能做的最好的事情就是将所有成员访问权折叠到一个函数中。假定不检查所有内容是否都是指针:

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)

没关系 不幸的是,没有->?或没有.?类似的运营商,所以我们有点不得不工作。

  • :))绝对正确,`auto r = wrap {p}-&gt; *&P :: q-&gt; *&Q :: r; r`就像古老的手稿一样清晰,被遗忘了很长的舌头,埋在数千年的尘土中,被200代所fe食-在一个尚未发现的锁隐窝中。 (6认同)
  • 我将 Barry 的答案标记为已接受的答案,因为这部分“不幸的是没有 -&gt;?或 .? 像运算符...”感谢大家考虑我的问题!如果有人想出一种方法来更好地模仿 C++ 中的安全导航操作符,那就太棒了。 (2认同)