Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the differences between these 4 lambda expressions?

I know this could seem very stupid for non-noob C++ developers, but what are the differences between these 4 lambda expressions? Code:

#include <iostream>
#include <math.h>
#include <functional>

inline double MyFunction(double a, double b, double c) {
    return (a + b + c);
}

inline void FunctionWrapper(std::function<double(double)> tempFunct, double value) {
    std::function<double(double)> funct;

    funct = tempFunct;

    std::cout << "result: " << funct(value) << std::endl;
}

int main()
{    
    double value = 100.0;

    FunctionWrapper([](double value) { return MyFunction(value, 1.0, 2.0); }, value);
    FunctionWrapper([](double value) -> double { return MyFunction(value, 1.0, 2.0); }, value);

    FunctionWrapper([value](double value) { return MyFunction(value, 1.0, 2.0); }, value);
    FunctionWrapper([value](double value) -> double { return MyFunction(value, 1.0, 2.0); }, value);
}

It seems it does the same? Either using two different "notations" and using the value as closure?

like image 905
markzzz Avatar asked Dec 04 '22 19:12

markzzz


2 Answers

In this context, they all produce the same results. However, there are logical differences between them.

  • [](double value) { return MyFunction(value, 1.0, 2.0); }

    This is a lambda which takes a single parameter of type value and passes this into MyFunction. Its return type is deduced from the return statement to be that of MyFunction, which is double.

  • [](double value) -> double { return MyFunction(value, 1.0, 2.0); }

    This is the same lambda as before, but this time its return type is explicitly specified to be double. It's the same in this case, but it would be different from the first one if the return type of MyFunction was something else. In that case, the first one would return what MyFunction returns, while this one would still return double.

  • [value](double value) { return MyFunction(value, 1.0, 2.0); }

    This one depends on the standard version used. In C++11 and 14, this one captures main's local variable value. However, that capture is hidden by the lambda's parameter value, so it's effectively useless. It would be different if the lambda had been declared as e.g. [value](double v) { return MyFunction(value, 1.0, 2.0); }. This would have passed on the captured value, and not its parameter.

    In C++17 and above, this was changed and it's actually ill-formed (compilation error). Naming a lambda parameter the same as something you capture is no longer allowed.

    Since the change was a defect report (CWG 2211), it applies retroactively and so it's legal for compilers to reject such code even in earlier C++ versions.

  • [value](double value) -> double { return MyFunction(value, 1.0, 2.0); }

    Same as lambda number 3, with explicit return type specification (the difference between 3 and 4 is exactly the same as between 1 and 2).

like image 144
Angew is no longer proud of SO Avatar answered Dec 28 '22 09:12

Angew is no longer proud of SO


The second lambda differs from the first in that you've specified the return type explicitly. Since the deduced return type of the first lambda is the same, there is no difference.

The third and fourth lambdas are ill-formed, since they declare a parameter with same name as a capture. See the standard rule:

[expr.prim.lambda.capture]

If an identifier in a simple-capture appears as the declarator-id of a parameter of the lambda-declarator's parameter-declaration-clause, the program is ill-formed. [ Example:

void f() {
  int x = 0;
  auto g = [x](int x) { return 0; }    // error: parameter and simple-capture have the same name
}

— end example  ]

This wording was adopted in C++17.

like image 26
eerorika Avatar answered Dec 28 '22 10:12

eerorika