第334章 std::optional との合わせ技

std::invoke の使い方としてありそうなのが std::optional と組み合わせることです。

まあ実際に std::invoke が絶対必要かというと、そんなことはないんですけど、std::invoke は可変長引数を受け取れますんで、比較的使い勝手は悪くないと思います。

てなことで std::optional を std::invoke でコールする実装例を見てみましょう。

注記

マクロについては クロスコンパイルのための組み込みマクロ を参照くださいね。

main.cpp. 

  1 #include <optional>
  2 #include <functional>
  3 #include <iostream>
  4
  5 template<typename T, typename U>
  6 auto call(T t, const std::optional<U> & opt)
  7 {
  8   if(opt){
  9     return std::make_optional(std::invoke(t,*opt));
 10   } else {
 11     return std::optional<std::invoke_result_t<T,U>>{};
 12   }
 13 }
 14
 15 template<typename T, typename U>
 16 auto call(T t, const std::optional<U> && opt)
 17 {
 18   if(opt){
 19     return std::make_optional(std::invoke(t,std::move(*opt)));
 20   } else {
 21     return std::optional<std::invoke_result_t<T,U>>{};
 22   }
 23 }
 24
 25 template<typename T>
 26 auto call(T t){
 27   return [t](auto &&param) -> decltype(auto) {
 28     return call(t,std::forward<decltype(param)>(param));
 29   };
 30 }
 31
 32 int inc(int x) {
 33   return x+1;
 34 }
 35
 36 int main()
 37 {
 38   std::optional<int> x{100};
 39   std::cout << *call(inc)(x) << '\n';
 40 }

ビルドと実行結果. 

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

このソースコードは関数を実行するためのインターフェース部分だけのシンプルなものです。

単に inc 関数を実行しているだけですね。

 32 int inc(int x) {
 33   return x+1;
 34 }

call 関数は引数 opt を関数オブジェクト・関数ポインター t の引数として実行して、結果を std::optional として返すだけの関数です。

  5 template<typename T, typename U>
  6 auto call(T t, const std::optional<U> & opt)
  7 {
  8   if(opt){
  9     return std::make_optional(std::invoke(t,*opt));
 10   } else {
 11     return std::optional<std::invoke_result_t<T,U>>{};
 12   }
 13 }

std::invoke(t,*opt) は opt の逆参照から、std::optional の中にあるオブジェクトを引数としてとり出して、関数オブジェクト t を実行してるだけです。

後はフォワード関数としてアプリケーション側で実際に呼び出す call 関数を作っておきます。

 25 template<typename T>
 26 auto call(T t){
 27   return [t](auto &&param) -> decltype(auto) {
 28     return call(t,std::forward<decltype(param)>(param));
 29   };
 30 }

これによりラムダ式的を使うことで呼び出しと、戻り値の処理が楽になります。

 38   std::optional<int> x{100};
 39   std::cout << *call(inc)(x) << '\n';

少しわかりにくいかもしれませんが call の引数はラムダ式を実行するために 2 層になってますね。

Copyright 2017-2018, by Masaki Komatsu