第333章 std::optional を使った関数オブジェクトのバインド

C++17 以前から optional 型の使い方として Haskell に近い構文を再現することに使えんじゃろか?

という勢力がいました。

「>>」に類似する「>>=」という演算子を実装すると関数オブジェクトのバインドを円滑にできるという考えがあったからです。

まあ文章で説明せずともソースコードをみればわかると思います。

main.cpp. 

  1 #if __cplusplus < 201703L
  2 # pragma message "-std=c++17を指定してください"
  3 #endif
  4
  5 #include <optional>
  6 #include <iostream>
  7
  8 template<typename T, typename U>
  9 auto operator>>=(std::optional<T> const& op, U func) -> decltype(func(*op))
 10 {
 11   if(op){
 12     return func(*op);
 13   } else {
 14     return {};
 15   }
 16 }
 17
 18 std::optional<int> foo(int a){
 19   return a;
 20 }
 21 std::optional<int> bar(int b, int c){
 22   return b + c;
 23 }
 24
 25 int main()
 26 {
 27   std::optional<int> result = foo(3) >>= [=](int b)
 28   {
 29     return foo(4) >>= [=](int c)
 30     {
 31       return bar(b,c);
 32     };
 33   };
 34
 35   std::cout << *result << '\n';
 36 }

出力. 

$ g++ main.cpp -std=c++17
$ ./a.out
7

えーとですね。

まずは 2 つの関数をバインドしたいとします。

 14 std::optional<int> foo(int a){
 15   return a;
 16 }
 17 std::optional<int> bar(int b, int c){
 18   return b + c;
 19 }

ちなみに foo と bar は関数として使いますが std::optional を返してますよね…

なので foo(1) とか bar(1,2) とかの返り値型は std::optional になるって仕組みです。

これを演算子オーバーロードを使って、関数オブジェクト(ラムダ式)とチェイン(連鎖)するというのが、関数型プログラミング風味の実装です。

以下の二項演算子オーバーロードでは、演算子の左辺に std::optional 型の op、右辺には関数オブジェクト func がきます。

  4 template<typename T, typename U>
  5 auto operator>>=(std::optional<T> const& op, U func) -> decltype(func(*op))
  6 {
  7   if(op){
  8     return func(*op);
  9   } else {
 10     return {};
 11   }
 12 }

つまり左辺を引数にして func を実行し、さらにその返り値の型も std::optional となります。

ということは「 >>= 」を使い続けても常に左辺が std::optional となります。

例えば以下のような感じで演算子を連鎖させることができます。

 27   std::optional<int> result = foo(3) >>= [=](int b)
 28   {
 29     return foo(4) >>= [=](int c)
 30     {
 31       return bar(b,c);
 32     };
 33   };

さらに効果を保持したままに >>= を追加するなんてこともできます。

  std::optional<int> result = foo(3) >>= [=](int b)
  {
    return foo(4) >>= [=](int c)
    {
      return bar(b,c) >>= [=](int d)
      {
        return d;
      };
    };
  };

まあ意味はないのですが、このように >>= をつけることで Haskell らしき構文にすることができます。

Copyright 2017-2018, by Masaki Komatsu