是否有安全的导航操作员C++?
Is there a safe navigation operator for C++?
在现代C++中,有没有办法进行安全导航?
例如,而不是做...
if (p && p->q && p->q->r)
p->q->r->DoSomething();
。通过使用某种短路智能指针或利用运算符重载的某种其他类型的语法,或标准C++库或 Boost 中的某些内容,具有简洁的语法。
p?->q?->r?->DoSomething(); // C++ pseudo-code.
上下文尤其C++17。
你能做的最好的事情就是将所有成员访问折叠到一个函数中。这假设没有检查所有内容都是指针:
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;
}
}
这使您可以编写:
if (auto r = access(p, &P::q, &Q::r); r) {
r->doSomething();
}
没关系。或者,您可以对运算符重载进行一些疯狂处理,并生成类似以下内容的内容:
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; }
};
这使您可以编写:
if (auto r = wrap{p}->*&P::q->*&Q::r; r) {
r->doSomething();
}
那也没关系。不幸的是,没有像运算符->?
或.?
,所以我们不得不绕过边缘。
"带有一点样板...">
我们可以得到这个:
p >> q >> r >> doSomething();
这是样板...
#include <iostream>
struct R {
void doSomething()
{
std::cout << "somethingn";
}
};
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();
}
由此产生的程序集非常可以接受。到这一点的旅程可能不是...
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
虽然现在是 2022 年,但仍然没有语言级别的支持。 我可以弄清楚一个非常接近的模拟:
template <typename T, typename F>
auto operator->*(T&& t, F&& f) {
return f(std::forward<T>(t));
}
#define pcall(fn)
[&](auto&& __p) {
if constexpr (std::is_pointer_v<std::remove_reference_t<decltype(__p)>>)
return __p ? __p->fn : decltype(__p->fn){};
else
return __p ? __p.fn : decltype(__p.fn){};
}
struct A {
int v;
int foo(int a, int b) { return a + b + v; }
};
struct B {
A* getA() { return &a; }
A a{100};
};
struct C {
B* getB() { return &b; }
B b;
operator bool() { return true; }
};
int main(){
int v = 3;
C by_val;
int t1 = by_val->*pcall(getB())->*pcall(getA())->*pcall(foo(1, v));
C* by_ptr = &by_val;
int t2 = by_ptr->*pcall(getB())->*pcall(getA())->*pcall(foo(1, v));
}
https://godbolt.org/z/zPhzTTv9s
相关文章:
- <<操作员在下面的行中工作
- C++ 与操作员不匹配<<
- 操作员C++的模棱两可的过载
- C++中>>操作员过载时出现问题?
- NaN 上的宇宙飞船操作员
- 比根<操作员
- SFINAE不能防止模棱两可的操作员过载吗?
- 什么是现实中的"endl"(或任何输出操纵器)?它是如何实现的,它如何与操作员<<一起工
- 为什么"delete"操作员给我访问权限冲突
- 为什么使操作员成为新的专用会打破 std::shared_ptr?
- 在这种情况下是私有的吗?试图使操作员<<过载
- C++操作员过载>>
- 是否有 C++20 浮点数的包装器,使我能够默认宇宙飞船操作员?
- 与异常处理程序中的操作员<<不匹配
- 复印作业操作员说明
- 友谊和操作员+=重载
- >操作员在比较两个C++容器时会怎么做?
- 与操作员比较两个计时时间点
- C++操作员订单评估
- 如何强制新操作员根据我的需要解释语句