C++11でC#っぽいnull許容型を実装してみた

null許容型って便利だけど、C++にはないよね？
ってことで、C#の実装を参考にテンプレートを使って実装してみました。

スマートポインタでよくね？
-> いや、ほらnull許容型のほうが用途がわかりやすいとか・・ね・・・
　　単にテンプレートとか、演算子のオーバーライドとかの理解を確かめるために作っただけです。

ソースコードは以下においてます。
[nullable.hpp](https://github.com/totoho03/totoho_libs/blob/master/cpplibs/nullable/nullable.hpp)

参考サイト:
[Null 許容型 (C# プログラミング ガイド)](https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/nullable-types/)

## 何がうれしいの？

今まで

```cpp
typedef struct{
  double value;
  bool   has_value;
] nullable_double_t;

nullable_double_t nd = {};

// なんか処理(この間にndに値を代入したり)

if(nd.has_value) {
  // 値が設定されていたら実行する処理
}
```

ってのが、↓のようにかけるようになる。

```cpp
#include <nullable.hpp.

nullable<double> dn;

// なんか処理(この間にndに値を代入したり)

if(nd) {
  // 値が設定されていたら実行する処理
}
```

って書けたらいい感じだよね？ってのが実装した動機です。

## 動作環境

タイトル通りC++11以降。
（たぶんOKなはず。記載されたコードの動作チェックは[repl.it](https://repl.it/)で確認しました)

## 使い方

nullable.hppをincludeして変数宣言。初期値はnullなので、条件式に放り込むと、falseになる。
値を使用するときは`get_value()`を使う。

```cpp
#include "nullable.hpp"

using namespace std;
using namespace totoho;

nullable<int> ni1;
if(ni1) {
  cout << "ni1 is " << ni1.get_value() << endl;
} else {
  cout << "ni1 is null..." << endl;
}

// ni1 is null...
```

宣言時に値を指定することも可能。

```cpp
nullable<double> nd1(0.5f);
if(nd1) {
  cout << "nd1 is " << nd1.get_value() << endl;
} else {
  cout << "nd1 is null..." << endl;
}
// nd1 is 0.5
```

nullptrを代入することで初期化できる。

```
nd1 = nullptr;
if(nd1) { 
  cout << "nd1 is " << nd1.get_value() << endl;
} else {
  cout << "nd1 is null..." << endl;
}
// nd1 is null...
```

`get_value()`を使わずにキャストすることも可能。

```cpp
nd1 = 2.1f;
if(nd1) {
  cout << "nd1 is " << (double)nd1 << endl;
}
// nd1 is 2.1
```

nullの状態で値を使用するとruntime_errorが投げられる

```cpp
nullable<double> nd2;
// cout << "nd2 is " << nd2.get_value() << endl; // runtime_error
```

## 実装について

基本的には構造体の例と同じく、型Tの変数とnullを示すためのフラグの2つのメンバ変数を持っています。

```cpp:nullable.hpp
template <typename T>
class nullable
{
private:
  bool has_value_;
  T value_;
  ...
```

コンストラクタは、デフォルトでは`has_value_`に`false`を設定するため、デフォルトコンストラクタを定義。

```cpp:nullable.hpp
nullable<T>(void)
: has_value_(false)
{}
```

あとは、`T`型と`nullable<T>`型をそれぞれ引数にとれるように以下を定義。
(追記)`explicit`指定子は`nullable<T> nd = 0.5;`みたいに初期化時に`T`型を設定できるように削除。

```cpp:nullable.hpp
nullable<T>(T val)
: has_value_(true)
  ,value_(val)
{}

nullable<T>(const nullable<T> &rhs)
: has_value_(rhs.has_value_)
  ,value_(rhs.value_)
{}
```

あとは2つのメンバ変数をset, getするI/Fと便利にするための演算子のオーバーライドが続くので、それぞれ説明。

### null値のsetter, getter


`nd1 = nullptr`みたいに、`nullptr`を使用して`has_value`フラグを`false`に設定できるように`void set_null(void)`と`void*`型を引数にとる`=`オペレーターのオーバーライド。

```cpp:nullable.hpp
void set_null(void)
{
  has_value_ = false;
}
...
nullable<T> &operator=(const nullptr_t rhs)
{
  has_value_ = false;
  return *this;
}
```

`if(nd1)`みたいにif文なんかの条件式で直接使用できるように、has_valueのgetterとして`bool has_value(void)`と、boolをオーバーライド。`explicit`をつけているけどboolのオーバーライドは[こことか](http://d.hatena.ne.jp/yohhoy/20121110/p1)にあるように、if文などの条件式では暗黙的に変換されるらしい（ややこしい）。

```cpp:nullable.hpp
bool has_value(void) const
{
  return has_value_;
}
...
explicit operator bool(void) const
{
  return (*this).has_value();
}
```

### T型, nullable<T>型のsetter, getter

`T`型、`nullable<T>`型を代入できるように`set_value`I/Fと`=`オペレータのオーバーライド。`T`型代入時は`has_value_`に`true`を自動的に設定。

```cpp:nullable.hpp
// T型代入用
void set_value(const T &rhs)
{
  value_ = rhs;
  has_value_ = true;
}
...
nullable<T> &operator=(const T &rhs)
{
  set_value(rhs);
  return *this;
}
```

```cpp:nullable.hpp
//nullable<T>型代入用
nullable<T> &operator=(const nullable<T> &rhs)
{
  set_value(rhs);    
  return *this;
}
...
void set_value(const nullable<T> &rhs)
{
  value_ = rhs.value_;
  has_value_ = rhs.has_value_;
}
```

最後に値を取り出すためのI/Fとして`get_value`とキャストして値を取り出せるように戻り値`T`型の単項演算子をオーバーライド。
`get_value`では`has_value`が`false`なら例外をスロー。


```cpp:nullable.hpp
T get_value(void) const
{
  if (!has_value_)
  {
    throw std::runtime_error("value is null!");
  }
  return value_;
}
...
explicit operator T(void) const
{
  return (*this).get_value();
}
```


## 今後の課題

~~演算子のオーバーライド周りが勉強不足なので、↓の書き方ができない。~~
~~どう書けばいいんだろ？~~

コンストラクタの`explicit`外すことで行けるそうです。
コードなおしてます。

```cpp
nullable<double> nd3 = 0.5f;
```

## さいごに

初投稿です。この業界も3年目で、やっぱりアウトプットを出す習慣をつけないとなぁということで、四半期に1本のペースでは記事を書けることを目標に頑張っていきます。