質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
86.12%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

解決済

ラムダ式の再帰でautoを使った場合の簡潔な書き方はあるのか?

raccy
raccy

総合スコア21683

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

2回答

0リアクション

1クリップ

7337閲覧

投稿2016/11/03 12:10

編集2016/11/03 12:11

この質問はC++14での話です。C++17からはこうなるみたいな情報もあれば嬉しいです。

###概要

C++14からラムダ式の仮引数にもautoが使えるようになりましたが、このautoを使ったときにラムダ式の再帰を簡潔に書く方法がわかりません。

###サンプルコード
下記コードはコラッツの問題において、何ステップで1になるかを数える処理です(負の値の場合とか細かいエラー処理とかは省いています)。

C++

#include <functional> #include <iostream> // C++11 での書き方 int collatz_count1(int x) { std::function<int(int)> f = [&f](int n) { if (n == 1) { return 0; } else if (n % 2 == 0) { return f(n / 2) + 1; } else { return f(3 * n + 1) + 1; } }; return f(x); } // C++14 でのみ動作 int collatz_count2(int x) { auto f = [](auto g, auto n) { if (n == 1) { return 0; } else if (n % 2 == 0) { return g(g, n / 2) + 1; } else { return g(g, 3 * n + 1) + 1; } }; return f(f, x); } // コンパイルエラー int collatz_count3(int x) { auto f = [&f](auto n) { if (n == 1) { return 0; } else if (n % 2 == 0) { return f(n / 2) + 1; } else { return f(3 * n + 1) + 1; } }; return f(x); } // コンパイルエラー int collatz_count4(int x) { auto f = [&f](int n) { if (n == 1) { return 0; } else if (n % 2 == 0) { return f(n / 2) + 1; } else { return f(3 * n + 1) + 1; } }; return f(x); } int main() { int i = 27; std::cout << "collatz_count1: " << collatz_count1(i) << std::endl; std::cout << "collatz_count2: " << collatz_count2(i) << std::endl; std::cout << "collatz_count3: " << collatz_count3(i) << std::endl; std::cout << "collatz_count4: " << collatz_count4(i) << std::endl; return 0; }

collatz_count1collatz_count2はうまく動きます(collatz_count1はC++11でも)が、collatz_count3 collatz_count4はコンパイルエラーになります。fの型をdecltype(auto)にコンパイルエラーになります。

###何が問題なのか?
もっと汎用的にするため、ラムダ式の仮引数を(auto n)のようにした場合、collatz_count1のような書き方はできなくなると思っています。なぜならstd::function<int(int)>みたいな型をどのようにも書けないからです(かといってstd::function<int(int)>と書いてしまうと、せっかくのautointに確定してしまいます)。となると上の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

以下のような質問にはリアクションをつけましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

リアクションが多い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
86.12%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。