Is there a safe navigation operator for C++?

喜你入骨 提交于 2019-12-01 07:40:15

The best you can do is collapse all the member accesses into one function. This assumes without checking that everything is a pointer:

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;
    }
}

Which lets you write:

if (auto r = access(p, &P::q, &Q::r); r) {
    r->doSomething();
}

That's ok. Alternatively, you could go a little wild with operator overloading and produce something like:

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; }
};

which lets you write:

if (auto r = wrap{p}->*&P::q->*&Q::r; r) {
    r->doSomething();
}

That's also ok. There's unfortunately no ->? or .? like operator, so we kind of have to work around the edges.

"With a little Boilerplate..."

We can get to this:

p >> q >> r >> doSomething();

Here's the boilerplate...

#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();
}

The resulting assembly is very acceptable. The journey to this point may not be...

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
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!