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