Null条件演算子とは
Null条件演算子とは、第一項がヌルポインタ (null pointer)でない場合に第二項の結果を返し、そうでない場合にnullを返す演算子である。nullでないことのチェックを回避し、メソッドチェーンやプロパティチェーンを行うために用いられる
https://ja.wikipedia.org/wiki/Null条件演算子
例えばC#の場合は以下のように?.
演算子を使用する
Result c = hoge?.piyo()?.foo()?.bar();
単なるメソッドチェーンで行う場合は、過程のメソッドでもしnullが返った場合エラーになってしまうのでnullチェックが必要になる。
その場合メソッドチェーンが使用できず、一時変数やif文が大量に入るということだ。
関数にして早期リターンするのが難しい場合はネストが深くなることもあるだろう。
Piyo piyo = hoge.piyo();
if(piyo != null) {
Foo foo = piyo.foo();
if(foo != null) {
...
}
}
C++にはNull条件演算子はない
そんな便利なNull条件演算子であるが残念ながらC++にはない。
なので、先ほどあげたように随時nullチェックをするしかない
if (auto * piyo = hoge->piyo()) {
if (auto * foo = piyo->foo()) {
...
}
}
ちょっとめんどくさい。
Null条件演算子的なものを作った
本題。
実装
細かい型チェックなどは省いているが、ラムダで遅延評価にしつつ、先にnullチェックだけするようなシンプルなものを作った
#pragma once
namespace safe_navigation
{
template<class T, class U>
auto operator | (T&& p, U&& func)->decltype(func(*p))
{
if (!p) {
return nullptr;
}
return func(*p);
}
}
一応これで生ポインタ、スマートポインタは対応できた。
std::optional
は未対応だが、必要な場合は返り値の型をチェックしてnullopt
を返せばいい
使用方法
使いたい時はusing safe_navigation::operator|
をすることで使用できるようになる。
int main()
{
using safe_navigation::operator|;
Hoge* hoge = nullptr;
if (auto * foo = hoge
| [](auto& hoge) { return hoge.piyo();}
| [](auto& piyo) { return piyo.foo(); }
) {
// foo something
}
return 0;
}
これでネストが深くなったりチェック用のローカル変数ができることもなくチェックができる
が、う~ん冗長な気もする
マクロにしてみる
え~い、マクロにしちゃえ~~
#define SAFE_NAVIGATION(method) [](auto& value){return value. method ;}
#define SAFE_NAVIGATION_T(type, method) [](type & value){return value. method ;}
C++11以前はジェネリックラムダが使用できないので、型を明示できるようにもした
int main()
{
using safe_navigation::operator|;
Hoge* hoge = nullptr;
if (auto * foo = hoge
| SAFE_NAVIGATION_T(Hoge, piyo()) // C++11以前
| SAFE_NAVIGATION(foo()) // C++14以降
) {
// foo something
}
return 0;
}
多少はましになったかもしれない。
まとめ
- C++でもNull条件演算子的なものが欲しい!!
- ラムダとか使えば無理やりそれっぽいことはできる