c++lambdafunctional-programmingcurryinggeneric-lambda

c++ lambda: Currying sum function: returns different results using capture by value vs by reference


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


Solution

  • 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