简体   繁体   中英

How do you implement a recursive function in a function in C++?

I understand how lambda functions work. The problem is that the program calls the function recursiveFunction() before the compiler has deduced what 'auto' should be. The thing is, it's a recursive function so the function itself is in the definition.

#include <iostream>
using namespace std;

template <class T>
class Class {
    public:
        int foo(int x);
};

template <class T>
int Class<T>::foo(int x) {
    auto recursiveFunction = [=](int n)->int {
        if (n <= 1) return 1;
        else return n*recursiveFunction(n-1);
    };
    return recursiveFunction(x);
}

int main() {
    Class<int> c;
    cout << c.foo(5) << endl;
    return 0;
}

I've also implemented this using a class using templates in case that factors into the problem.

Here's the error message:

main.cpp: In instantiation of 'int Class<T>::foo(int) [with T = int]':
main.cpp:21:20:   required from here
main.cpp:14:40: error: use of 'recursiveFunction' before deduction of 'auto'
         else return n*recursiveFunction(n-1);

Thanks!

Answered here :

The second snippet runs into [dcl.spec.auto]/10:

If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed.

The type of foo is needed to determine the type of the expression foo within the lambda body, but at that point you haven't deduced foo's type yet, so the program is ill-formed.

Further references:

Fix: https://godbolt.org/z/np3ULe

#include <iostream>
#include <functional>

template <class T>
class Class {
 public:
  int foo(int x);
};

template <class T>
int Class<T>::foo(int x) {
  std::function<int(int)> fac = [&fac](int n) -> int {
    if (n <= 1)
      return 1;
    else
      return n * fac(n - 1);
  };
  return fac(x);
}

int main() {
  Class<int> c;
  std::cout << c.foo(5) << std::endl;
  return 0;
}

Couple possible answers:

  1. type-erasure; you don't actually need to know the type of recursiveFunction, just enough to have pinned down its signature.

So you could just do without the problematic auto and associated deduction, and promise to know the type in advance.

template <class T>
int Class<T>::foo(int x) {
    std::function<int(int)> recursiveFunction;
    recursiveFunction = [=](int n)->int {
        if (n <= 1) return 1;
        else return n*recursiveFunction(n-1);
    };
    return recursiveFunction(x);
}
  1. If this wasn't just an oversimplified example, you don't appear to actually be capturing any state, so you could just use a normal recursive function instead of a lambda.
namespace {
    int recursiveFunction(int) {
        if (n <= 1) return 1;
        else return n*recursiveFunction(n-1);    
    }
}

int Class<T>::foo(int x) {
    return recursiveFunction(x);
}
  1. If the lambda aspect was actually important, you're looking for the "Y combinator". Which is not very straightforward in C++, but something like:
#include <iostream>
#include <functional>

template <class T>
class Class {
    public:
        int foo(int x);
};

template<class F>
struct function_traits;

template<class R, class T>
struct function_traits<R(T)> {
    typedef R return_type;
    typedef T arg_type;
};

// function pointer
template<class R, class... Args>
struct function_traits<R(*)(Args...)> : public function_traits<R(Args...)>{}};

template <typename Signature>
auto y (std::function<typename function_traits<Signature>::return_type(typename function_traits<Signature>::arg_type, std::function<Signature>)> f) 
    -> std::function<Signature>
{
    return [f](typename function_traits<Signature>::arg_type n) -> typename function_traits<Signature>::return_type { return f(n,y(f)); };
}

template <class T>
int Class<T>::foo(int x) {
   return y<int(int)>([=](int n, auto recursiveFunction) -> int {
        if (n <= 1) return 1;
        else return n*recursiveFunction(n-1);    
    })(5);
}

int main() {
    Class<int> c;
    std::cout << c.foo(5) << std::endl;
    return 0;
}

If you want to avoid std::function , you might do (requires C++14 for generic lambda):

int Class<T>::foo(int x) {
    auto recursiveFunction = [](auto recFunc, int n) -> int
    {
        if (n <= 1) return 1;
        else return n * recFunc(recFunc, n - 1);
    };
    return recursiveFunction(recursiveFunction, x);
}

Demo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM