1

I have a very simple recursive lambda which calculates sum of given 2 numbers:

auto sum_v1 = [](auto first){
  return [first](auto second){
    return first + second;
  };
};

sum_v1 (1)(2); // returns 3, OK

Now the same function, using capture by reference for arg first.

auto sum_v2 = [](auto first){
  return [&first](auto second){
    return first + second;
  };
};

sum_v2 (1)(2); // returns 4, NOT OK

sum_v2 gets the argument first as 2 and arg second is also 2.

I know how to get the right result. I could use either the sum_v1 OR sum_v3 (shown below).

// accept arg first using r-value reference
auto sum_v3 = [](auto&& first){
  return [first](auto second){
    return first + second;
  };
};

sum_v3 (1)(2); // returns 3, OK

How is sum_v2, while creating the lambda, is seeing the arg first as 2. I am struggling to understand that.

Could you please give me some hint(s) to understand this better? I am using gcc.9.2.0 with -std=c++17 on rhel 7.

Thanks, Gaurav

max66
  • 65,235
  • 10
  • 71
  • 111
User9102d82
  • 1,172
  • 9
  • 19
  • 1
    What makes you think "`sum_v2` gets the argument `first` as `2` and arg `second` is also `2`?" Testing I get a return of `3`? – David C. Rankin Feb 02 '20 at 01:42
  • @David: which compiler are you using? I have updated my details in my post. – User9102d82 Feb 02 '20 at 01:45
  • @David: Okay, I understand what you have said sir. You are right. sum_v2 is not getting the first argument as '2'. It does get it as '1'. My sentence is wrong. I will correct it now. Thank you for bringing it to my attention. Much appreciated. – User9102d82 Feb 02 '20 at 01:57
  • You have a point. On gcc 9.2 with `-std=c++17` I get `"warning: ‘first’ is used uninitialized in this function [-Wuninitialized]"` -- so there is a problem (either with the compile or with v2) – David C. Rankin Feb 02 '20 at 02:15

1 Answers1

3

This:

auto sum_v2 = [](auto first){
  return [&first](auto second){
    return first + second;
  };
};

Is undefined behavior, as you are taking a reference to a local variable first whose lifetime ends before it is used. As with all UB, anything could happen. On your machine it seems that first ends up referencing second instead, but that's not guaranteed. The code could crash on other systems, or produce the expected result, but you can't rely on it.

With -Wall -Werror you wouldn't even be able to compile this bad code. Demo: https://godbolt.org/z/3gYx7q

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • I am checking this but kindly note that the said flags don't make any difference and it still does compile. Here is output from my system: ` 20200126 18:23 [compuser@lenovoe470:singles]$ cat abc.cpp ; g++ -std=c++17 -Wall -Werror abc.cpp && echo [$(./a.out)] #include int main() { auto sum_v2 = [](auto first){ return [&first](auto second){ return first + second; }; }; std::cout << sum_v2 (1)(2) << std::endl; // returns 4, NOT OK }; [4] 20200126 18:23 [compuser@lenovoe470:singles]$ ` – User9102d82 Feb 02 '20 at 02:03
  • GCC will only flag this code if compiling with optimization enabled. See the demo link in my answer. – John Zwinck Feb 02 '20 at 02:05
  • Answer accepted. Now I understand the behavior. It was not evident to me before. Thank you also for showing me difference between no optimization & optimization turned on. I should now opt for -O3 -Werror -Wall flags when I am studying and testing. Cheers! – User9102d82 Feb 02 '20 at 02:32