この質問はC++14での話です。C++17からはこうなるみたいな情報もあれば嬉しいです。
###概要
C++14からラムダ式の仮引数にもauto
が使えるようになりましたが、このauto
を使ったときにラムダ式の再帰を簡潔に書く方法がわかりません。
###サンプルコード
下記コードはコラッツの問題において、何ステップで1になるかを数える処理です(負の値の場合とか細かいエラー処理とかは省いています)。
C++
1#include <functional> 2#include <iostream> 3 4// C++11 での書き方 5int collatz_count1(int x) 6{ 7 std::function<int(int)> f = [&f](int n) { 8 if (n == 1) { 9 return 0; 10 } else if (n % 2 == 0) { 11 return f(n / 2) + 1; 12 } else { 13 return f(3 * n + 1) + 1; 14 } 15 }; 16 return f(x); 17} 18 19// C++14 でのみ動作 20int collatz_count2(int x) 21{ 22 auto f = [](auto g, auto n) { 23 if (n == 1) { 24 return 0; 25 } else if (n % 2 == 0) { 26 return g(g, n / 2) + 1; 27 } else { 28 return g(g, 3 * n + 1) + 1; 29 } 30 }; 31 return f(f, x); 32} 33 34// コンパイルエラー 35int collatz_count3(int x) 36{ 37 auto f = [&f](auto n) { 38 if (n == 1) { 39 return 0; 40 } else if (n % 2 == 0) { 41 return f(n / 2) + 1; 42 } else { 43 return f(3 * n + 1) + 1; 44 } 45 }; 46 return f(x); 47} 48 49// コンパイルエラー 50int collatz_count4(int x) 51{ 52 auto f = [&f](int n) { 53 if (n == 1) { 54 return 0; 55 } else if (n % 2 == 0) { 56 return f(n / 2) + 1; 57 } else { 58 return f(3 * n + 1) + 1; 59 } 60 }; 61 return f(x); 62} 63 64int main() 65{ 66 int i = 27; 67 std::cout << "collatz_count1: " << collatz_count1(i) << std::endl; 68 std::cout << "collatz_count2: " << collatz_count2(i) << std::endl; 69 std::cout << "collatz_count3: " << collatz_count3(i) << std::endl; 70 std::cout << "collatz_count4: " << collatz_count4(i) << std::endl; 71 return 0; 72}
collatz_count1
とcollatz_count2
はうまく動きます(collatz_count1
はC++11でも)が、collatz_count3
とcollatz_count4
はコンパイルエラーになります。f
の型をdecltype(auto)
にコンパイルエラーになります。
###何が問題なのか?
もっと汎用的にするため、ラムダ式の仮引数を(auto n)
のようにした場合、collatz_count1
のような書き方はできなくなると思っています。なぜならstd::function<int(int)>
みたいな型をどのようにも書けないからです(かといってstd::function<int(int)>
と書いてしまうと、せっかくのauto
がint
に確定してしまいます)。となると上のcollatz_count3
のような書き方で動いて欲しいのですが、f
の型が確定していないためキャプチャできないとエラーになります。これはcollatz_count4
も同じで、同じエラーとなります。そこで、collatz_count2
のように、キャプチャではなく引数で渡せばうまく動きますが、簡潔とは言い難いです。
つまり、何がしたいかというと、
- ラムダ式の仮引数の型は
auto
にしてテンプレートのように使いたい。 - しかし、その場合の再帰はキャプチャでは実現できなくなるのをなんとかしたい。
なんとか方法はないのでしょうか?それとも現在のC++14の仕様ではcollatz_count2
のように書くしかないのでしょうか?
###検証環境
OS: macOS 10.12.1
GCC: Homebrew gcc 6.2.0
Clang: Apple LLVM version 8.0.0 (clang-800.0.42.1)
-std=c++14
付きでコンパイルしています。
###参考文献
C++ : ラムダ式のみでの再帰文 - Qiita
C++プログラマ キャスブログ
ラムダ式で再帰関数 - C++と色々
c++ - Recursive lambda functions in C++11 - Stack Overflow
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。