Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different results obtained by using generic lambdas and automatic return type feature in C++14

I am trying to realize this piece of high-order function in python using C++:

def add1(x):
    def helper():
        nonlocal x
        x += 1
        return x
    return helper

Here are the three versions I created:

#include <iostream>
#include <functional>

using namespace std;

function<int(void)> add1_v1(int x) {
    function<int(void)> g = [&x]() {return ++x;};
    return g;
}

auto add1_v2(int x) {
    function<int(void)> g = [&x]() {return ++x;};
    return g;
}

auto add1_v3(int x) {
    auto g = [&x]() {return ++x;};
    return g;
}

int main() {
  auto a = add1_v1(100);
  auto b = add1_v2(100);
  auto c = add1_v3(100);
  for(int i = 0; i < 3; ++i) {
      cout << a() << endl;
  }
  cout << "-------------------------------------------" << endl;
  for(int i = 0; i < 3; ++i) {
      cout << b() << endl;
  }
  cout << "-------------------------------------------" << endl;
  for(int i = 0; i < 3; ++i) {
      cout << c() << endl;
  }
  return 0; 
}

The outputs are:

101
102
103
-------------------------------------------
4239465
4239466
4239467
-------------------------------------------
4201325
4201325
4201325

Only add1_v1 matches what I want. Can anyone explain the reason for me?

like image 929
Chen Chen Avatar asked Feb 09 '17 03:02

Chen Chen


People also ask

What is generic lambda in C++?

Generic lambdas were introduced in C++14 . Simply, the closure type defined by the lambda expression will have a templated call operator rather than the regular, non-template call operator of C++11 's lambdas (of course, when auto appears at least once in the parameter list).

What is the advantage of lambda function in C++?

It is a convenient way to define an anonymous function object or functor. It is convenient because we can define it locally where we want to call it or pass it to a function as an argument. Lambda is easy to read too because we can keep everything in the same place.

How do lambda functions work in C++?

A lambda can introduce new variables in its body (in C++14), and it can also access, or capture, variables from the surrounding scope. A lambda begins with the capture clause. It specifies which variables are captured, and whether the capture is by value or by reference.

What is lambda capture in C++?

A capture clause of lambda definition is used to specify which variables are captured and whether they are captured by reference or by value. An empty capture closure [ ], indicates that no variables are used by lambda which means it can only access variables that are local to it.


2 Answers

The reason is that this is undefined behavior.

The inner lambda captures x by reference.

The problem is that as soon as add() returns, its parameter gets destroyed, and the returned lambda has a dangling reference to a destroyed object.

The lambda must capture x by value; and what it looks to me you're really trying to do here is a mutable lambda:

auto add(int x) {
    function<int(void)> g = [x]() mutable {return ++x;};
    return g;
}

Note that this approach carries certain implications when it comes to subsequently copying the returned lambda; but as long as the returned lambda remains "in one place", throughout its remaining lifetime, the resulting semantics will probably be what you expect.

like image 160
Sam Varshavchik Avatar answered Sep 30 '22 17:09

Sam Varshavchik


All of them are ill-formed, because you're capturing x by reference in lambda, but x is a local variable and would be destroyed when get out of the function add, then the reference becomes dangle, dereference on it latter causes UB, which means anything is possible; even the 1st case seems works fine.

like image 23
songyuanyao Avatar answered Sep 30 '22 16:09

songyuanyao