Capture by value in recursive lambda

We can define a recursive lambda function, for example,

std::function<void(int)> fun = [&fun](int a) {  if (a) fun(a - 1); };

then we can call him

fun(10);

However, if I change the definition to

std::function<void(int)> fun = [fun](int a) {  if (a) fun(a - 1); };

and then try calling using

fun(10);

segmentation error occurs.

Can someone explain why capture by reference works, and capture by value gives a segmentation error.

+4
source share
2 answers

Value capture is evaluated as part of the evaluation of the lambda expression. At that time fun, it was still not initialized, because you are still evaluating its initializer. Only after this is funinitialized, but by then a copy has already taken place.

, -, fun, fun, uninitalized std::function — Undefined .

+8

std::function . .

. Undefined , .

, Euclid gcd() . , :

int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a%b);
}

, . this this ( , -, ). , ?

std::function

std::function:

std::function<int(int, int)> gcd = [&](int a, int b){
    return b == 0 ? a : gcd(b, a%b);
};

, . ( ), ( gcd gcd , ), .

:

auto gcd_self = std::make_shared<std::unique_ptr< std::function<int(int, int)> >>();
*gcd_self = std::make_unique<std::function<int(int, int)>>(
  [gcd_self](int a, int b){
    return b == 0 ? a : (**gcd_self)(b, a%b);
  };
};

( ), /, . , .

Y-combinator

:

template <class F>
struct y_combinator {
    F f; // the lambda will be stored here

    // a forwarding operator():
    template <class... Args>
    decltype(auto) operator()(Args&&... args) const {
        // we pass ourselves to f, then the arguments.
        // the lambda should take the first argument as `auto&& recurse` or similar.
        return f(*this, std::forward<Args>(args)...);
    }
};
// helper function that deduces the type of the lambda:
template <class F>
y_combinator<std::decay_t<F>> make_y_combinator(F&& f) {
    return {std::forward<F>(f)};
}
// (Be aware that in C++17 we can do better than a `make_` function)

gcd :

auto gcd = make_y_combinator(
  [](auto&& gcd, int a, int b){
    return b == 0 ? a : gcd(b, a%b);
  }
);

y_combinator -, , , . , .

, "recurse" . , .

y_combinator , , "recurse" ( , y_combinator) . , y_combinator, .

:

auto foo = make_y_combinator( [&](auto&& recurse, some arguments) {
  // write body that processes some arguments
  // when you want to recurse, call recurse(some other arguments)
});

.

( ) @Barry .

+1

Source: https://habr.com/ru/post/1672374/


All Articles