メンバ関数をもっと分ける

空いているので、短い軽い話をします。

前日の記事は @Nabetani さんによる[「C++ ライフをよりハッピーにするための 14の小ネタ」](https://qiita.com/Nabetani/items/4129e9a694883b330a64)、
翌日の記事は @nomunomu0504 さんによる[「CとC++が混在したプログラムでの注意点」](https://qiita.com/nomunomu0504/items/722a2771fef7d8038ceb)です。

## const, nonconst

こちらはよく知られていると思いますが、オブジェクトが`const`かそうでないかによって呼ばれる時のメンバ関数を分けることができます。その場合、以下のようにメンバ関数の後ろに`const`を付けます。

```cpp
struct X
{
    int& value()       noexcept {return v;}
    int  value() const noexcept {return v;}
  private:
    int v;
};

X x = make_x(42);
x.value(); // int& X::value() noexcept が呼ばれる

const X x = make_x(42);
x.value(); // int X::value() const noexcept が呼ばれる
```

`const`なオブジェクトには変更操作が出来ませんが、変更操作を伴わないメンバ関数に`const`をつけておけば、`const`なオブジェクトのそのメンバ関数を呼ぶことが可能です。

`const`メンバ関数で直接、`mutable`でない値を変更しようとすると、エラーが出ます（当然）。また、`const`メンバ関数で`mutable`な参照を返そうとすると、`discard qualifier`みたいなエラーが出ます。`const`メンバ関数からメンバの値を取ろうとするときは、中身を変更できないような返し方（コピーとか`const&`とか）しかできません。

値が変更されるかどうかを明示することは、可読性やバグ回避の点から常によいことです。

## volatile, nonvolatile

私は使ったことがないですが、`volatile`も`const`と同様にしてメンバ関数を分けることが可能です。使ったことがないので憶測ですが、何らかのスマートなリソース管理機構か何かを作るときに、デバイスを触っている（`volatile`になっている）ときはちゃんと毎回見に行くけれど、そうでないときはデータをキャッシュしておく、というような抽象化ができたりするかもしれません。知らんけど。

## lvalue, rvalue reference

さて、本題ですが、左辺値と右辺値でもメンバ関数を分けることが可能です。

### 右辺値のメンバアクセスでメンバをムーブして返す

```cpp
struct X
{
    std::vector<int>&       values() &      noexcept {return v;}
    std::vector<int> const& values() const& noexcept {return v;}
    std::vector<int>&&      values() &&     noexcept {return std::move(v);}
  private:
    std::vector<int> v;
};

std::vector<int> v = make_x(/*...*/).values(); // move 版が呼ばれる
```

上記の例では、オブジェクトが右辺値で呼ばれた時は中身をムーブして返すようになっています。

これは、例えば以下のように明示的に`std::move`を呼べば

```cpp
struct X
{
    std::vector<int>&       values()       noexcept {return v;}
    std::vector<int> const& values() const noexcept {return v;}
  private:
    std::vector<int> v;
};

auto v = std::move(make_x().values()); // 左辺値参照を取得して、ムーブする
```

ちゃんとムーブできるわけですが、あまりにも面倒です。右辺値のメンバアクセスをしているのにまた`std::move`を書くのは面倒ですよね？　それを自動で解決するためにこの機能が使えます。

例えば、GNUのC++標準ライブラリにある`optional`の`operator*`や`value()`などはこういうことをしています。関数の戻り値になっている`optional`から値を取り出そうとする、というようなケースを想定しているのではないでしょうか。

### 代入制御

他にも、左辺値でしかできない操作、例えば代入などを禁止することができます。

```cpp
struct X
{
    X& operator=(int i) & {v = i; return *this;}
    X& operator=(int i) && = delete; // 自分が右辺値だった場合、代入不可
  private:
    int v;
};
```

あるいは、ムーブして返すこともできない所有リソースへの参照を取得する場合なども、似たようにして上手く制御できそうです。テンポラリなオブジェクトに対して呼び出してはいけない関数に全部同じことをしておけば、間違えて呼んでしまったミスを全てコンパイル時に落とすことができます。

うっかりミスをコンパイル時にエラーとして検出できるようになるのは、常に良いことです。

### コンビネータをサポートする

C++17では`optional`や`variant`が入って、エラー処理がいい感じになっていきそうな感じがしています。ところで、こういったものが入ると、以下のようなコードを書きたくならないでしょうか。

```cpp
foo::result<std::int32_t, std::exception_ptr> read_id();
std::map<std::int32_t, std::string> username;

auto value = read_id().and_then([&](std::int32_t id)
     -> foo::result<std::string, std::exception_ptr> {
        if(username.count(id) == 0) {
            return foo::err(std::make_exception_ptr(std::out_of_range("user ID not found")));
        }
        return foo::ok(username[id]);
    });
```

これは、失敗するかもしれない関数（`read_id`）を使ってIDを読み込み、それが成功したら（`and_then`）テーブル（`username`）を見て、IDがなければエラーメッセージ（`out_of_range`）を、あればユーザー名（`username[id]`）を、そもそも読み込みに失敗していたらその時点でのエラーメッセージ（read_idの返り値のエラーの方）を返す、というものです。

こういうことをするために、`std::variant`を使って自前の`result`型を作ったとします。これは、成功時の型か失敗時の型かのどちらかを持つクラスなので、`std::variant`をラップして便利関数をいくつか生やすと作れます。

この関数でオブジェクトが右辺値の時には`and_then`で受け取った関数オブジェクトに中身をムーブして渡す、というような制御も、ref-qualifierを使えば実装可能です。

```cpp
template<typename T, typename E>
struct result
{
    template<typename F>
    std::invoke_result_t<F, T&&> and_then(F&& f) &&
    {
        if(this->is_err())
        {
            return std::move(std::get<E>(this->value_));
        }
        return f(std::move(std::get<T>(this->value_)));
    }
};
```

本当に実装するなら変換とかもっとちゃんとしないと駄目ですが。

## まとめ

メンバ関数をオブジェクト自身の状態で分けて最強のクラスを作ろう！

## 関連

- https://cpprefjp.github.io/lang/cpp11/ref_qualifier_for_this.html
    - C++11で追加された右辺値参照版オーバーロードに関するcpprefjpの言語機能紹介。
- http://d.hatena.ne.jp/yohhoy/20160417/p1
    - 右辺値参照版オーバーロードの使い道、メンバ変数のムーブと代入制御の紹介
- https://qiita.com/kktk-KO/items/7f03dfdf7cc020aa2dc5
    - メンバ関数qualifierの紹介と、規格文書中の対応箇所、微妙なケースの事情など
- https://cpplover.blogspot.com/2009/12/rvalue-reference.html
    - メンバ関数へのref-qualifierの紹介。