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 &¶m) -> 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 &¶m) -> 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