C# には 2 種類の null 伝播があります。1つ目はあなたが説明したとおりです。ここで、がnullの場合foo
はnullを返しますが、nullでfoo
ない場合はを返しfoo->bar
ます。
これは、C++ ですでにそれほど複雑でも読みにくくもありません。
return (foo) ? (foo->bar) ? foo->bar->baz : nullptr : nullptr;
上記の方法でインライン条件をネストできるようにするマクロを作成できないようです。
#define Maybe(X, Y) (X)?X->Y:nullptr
// Attempted nested usage:
return Maybe(foo, Maybe(foo->bar, baz));
// ^ VS2015 complains here saying "Expected a member name".
しかし、マクロがいかに冗長であるかを考えると、たとえそれが機能したとしても、元々使用されていたインライン条件よりも優れているとは思えません。
ただし、Mooing Duck が回答で指摘した「二重呼び出し」の問題を回避するテンプレート ソリューションもあります。
template<typename T_Return, typename T_X, T_Return* T_X::*Y>
T_Return* Maybe(T_X* pX)
{
return (pX) ? pX->*Y : nullptr;
}
//Usage:
// Type is required, so assume the struct Foo contains a Bar* and Bar contains a Baz*.
return Maybe<Baz, Bar, &Bar::baz>(Maybe<Bar, Foo, &Foo::bar(foo));
もちろん、これは要件を満たしていますが、ばかげていて鈍いです。
ただし、メソッドの呼び出しに使用される 2 番目のタイプの Null Propagation があることに注意してください。つまり、"else" ケースは必要ありません。これは次と同等です。
// C# code here.
if(foo != null) { foo.Bar(); }
C# では次のことができます。
foo?.Bar()
これは、マクロを使用して複製を試みるのがはるかに簡単な機能であり、通常の構文によりよく「適合」します。
// C++ Null Propagation
#define Maybe(X) if (auto tempX = (X)) tempX
//Usage
Maybe(GetFoo())->Bar();
//Or for setting a variable:
Maybe(GetFoo()->bar = new Bar();
?: 省略形を使用しておらず、代わりに通常のインライン if ステートメントを使用しているため、機能します。
これは、メソッドの呼び出しまたはメンバーの設定のみを目的としていることに注意してください。
ただし、本当にしつこい場合は、次のようなことができます。
#define ReturnMaybe(X, Y) auto tempX = X; \
if (tempX != nullptr) \
{ \
return tempX->Y; \
} \
else \
{ \
return nullptr; \
} \
次のようにインライン化できます。
#define ReturnMaybe(X, Y) auto tempX = X; if(tempX) { return tempX->Y; }else{return nullptr;}
// Usage
Bar* GetBar()
{
ReturnMaybe(GetFoo(), bar)
}
しかし、これを連鎖させることはできず、ほぼすべての可能性 (おそらくメソッド呼び出しの null 伝播を除く) の冗長性を考慮すると、上記のソリューションのいずれかで問題を正確に解決することはできません。
編集 - テンプレート化された回答をわずかに改善しました。
編集 2 - これについて同僚と話していて、いくつかの他のオプションを考え出しました:
#define Maybe(X) (X==nullptr) ? nullptr : X
使用法:
return Maybe(foo)->bar;
興味深いことに、これは呼び出しメソッドの場合にも機能します。
Maybe(foo)->Bar();
展開するものが有効であるため (奇妙ではありますが)
(foo == nullptr) ? nullptr : foo->Bar();
ただし、?: 省略形で変数宣言を入れることができないため、保護することはできません。
return Maybe(get_next_parent())->child;
Mooing Duck が説明したように、一時的なものを自分で作成する必要があります。