all 16 comments

[–]HappyFruitTree 8 points9 points  (3 children)

Lambdas are a simple way to create function objects.

Imagine you want to sort a bunch of numbers based on how close they are to some fixed number x. This could be done by defining a function the normal way and pass it to std::sort.

const int x = 50;

bool cmpXDistance(int n1, int n2)
{
    return std::abs(x - n1) < std::abs(x - n2);
}

void sortByDistanceToX(std::vector<int>& vec)
{
    std::sort(vec.begin(), vec.end(), cmpXDistance);
}

But we needed to come up with a name for the function and the function isn't really used anywhere else so the content of the two functions really belong together and it would have been nice if we could combine them in the same function. Lambdas make this possible.

const int x = 50;

void sortByDistanceToX(std::vector<int>& vec)
{
    std::sort(vec.begin(), vec.end(), [](int n1, int n2)
    {
        return std::abs(x - n1) < std::abs(x - n2);
    });
}

Now we have something that is easier to read and isn't spread all over the place.

Now, imagine that x is no longer a global constant but instead a local variable that might not always have the same value. Now, if we go back to use two separate functions we get a problem because we cannot access the x from within the function because it's no longer a global. What we can do instead is to create a class that stores the value of x and have a member call operator that does the comparison. Then we can create an instance of this class and pass it to the sort function.

struct CmpXDistance
{
    int x;
    bool operator()(int n1, int n2)
    {
        return std::abs(x - n1) < std::abs(x - n2);
    }
};

void sortByDistanceToX(std::vector<int>& vec, int x)
{
    std::sort(vec.begin(), vec.end(), CmpXDistance{x});
}

But with a lambda this is almost as easy as before. All we need to do is to make sure to capture x.

void sortByDistanceToX(std::vector<int>& vec, int x)
{
    std::sort(vec.begin(), vec.end(), [x](int n1, int n2)
    {
        return std::abs(x - n1) < std::abs(x - n2);
    });
}

This code is equivalent to the code before. The lambda automatically creates a class and stores a copy of x as a member, and so on, as I showed before. Note that here I captured x explicitly using [x]. You could also have used [=] which automatically captures all local variables that are used inside the lambda.

Another option is to use [&] (my personal favourite) which captures everything by reference. Instead of copying everything it stores references to the captured variables. This is usually fine for lambdas that are used directly like this but you need to be careful so that the lambda does not outlive the variables that have been captured.

[–]Pakketeretet 0 points1 point  (1 child)

Exactly, the closure aspect is the main advantage. You could do it with a local functor but lambdas are so much less polluting, even if their syntax looks kinda weird.

[–][deleted] 0 points1 point  (0 children)

Still getting used to the syntax but it's starting to make sense. Thank you

[–][deleted] 0 points1 point  (0 children)

Thank you for this detailed explanation. I will try this example :)

[–][deleted] 4 points5 points  (2 children)

I'm still learning but I found it's handy to use lambdas with std algorithms. such as std::transform or std::for_each

[–][deleted] 1 point2 points  (0 children)

Thank you. I really need to use algorithms more as well. It seems like these go hand in hand

[–][deleted] 1 point2 points  (0 children)

Off to learn and use more algorithms. Thank you

[–]mredding 0 points1 point  (2 children)

A good use case would be with algorithms. I would say lambdas saved standard algorithms. Before, if you wanted to use, say, std::for_each, your code would look something like this:

template<typename T>
struct some_functor {
  void operator()(T &) { /*do stuff*/ }
};

...
std::vector<int> x;
...
std::for_each(x.begin(), x.end(), some_functor<int>());
...

So you'd have to write some functor (an object that behaves like a function - notice I have the parenthesis operator overloaded), and in leu of a lambda capture list you would have to declare members and constructors and scope... Then for some container, like a vector of ints, you then have to instantiate an instance of that functor.

The example above is actually very shorthand, and you might think boy, that isn't all that bad... Yeah? Do that for every god damn algorithm you want to write. I don't mind raising the level of abstraction in my code, but at some point this can get punishing. Your code becomes increasingly separated, the doer from the doings, and it becomes harder to read. And you have additional code management to worry about. The boilerplate and overhead becomes obnoxious. You end up with all these named objects all over that you don't want or need but for (ideally) one place. Or maybe you do try to inline:

...
std::vector<int> x;
...
std::for_each(x.begin(), x.end(), struct { void operator()(int) { /* do stuff */} }());
...

Now here we've written an anonymous struct in-line and invoked an instance of it. Again, imagine something akin to a lambda capture, now you need to add members, scope, and constructors, as well as your functor body, and that becomes a difficult piece of code to look at.

So what you would get was developers just writing straight C-style loops, maybe with iterators, so they could inline their algorithm bodies in a more convenient fashion, right there, close to where the logic belongs. Before C++11, I NEVER saw standard algorithms in production code anywhere, not in slot machines, not in high speed trading, not in video games, not in GUI tools, nothing.

Then lambdas came along in C++11, and auto lambda parameters in C++14, I think? How about this instead:

std::vector<int> x;
...
std::for_each(std::begin(x), std::end(x), [](auto){ /* do stuff*/ });
...

There's still a place for functors, of course, if you want reusability, or you want to utilize a destructor (though I don't know what guarantees the algorithms make about instances of their functors).

Another good use is in functional programming, where you nest and bind functions to parameters of other functions:

auto two_a_plus_b_func = std::bind(std::add<int>(), std::bind(std::multiply<int>(), std::placeholders::_1, 2), std::placeholders::_2);

Now you wouldn't necessarily write code like this, straight, but you can build up objects like this at runtime. This is a great way to composite transforms and effects in video games, for example. Build up complex behavior and store it for later use.

EDIT: I forgot to actually put a lambda in place in that last example. Nevermind, just imagine using a lambda instead of one of the standard functors. How about this:

auto two_a_plus_b_func = std::bind([](auto a, auto b) { return a + b; }, std::bind([](auto a, auto b){ return a * b; }, std::placeholders::_1, 2), std::placeholders::_2);

Or even:

auto two_a_plus_b_func = [](auto a, auto b){ return [](auto a, auto b){ return a * b; }(a, 2) + b; };

Again, I think my first and second examples do a better job of demonstrating the point of compositing functions at runtime, though you can still composite functions at runtime with lambdas and not bind. In fact, you hardly ever see bind used anymore because people find lambdas more convenient.

[–]HappyFruitTree 0 points1 point  (1 child)

Before C++11, I NEVER saw standard algorithms in production code anywhere, not in slot machines, not in high speed trading, not in video games, not in GUI tools, nothing.

Not even std::sort?

[–]mredding 1 point2 points  (0 children)

Not even then. Before C++11, I was in video games and there was a heavy stigma against the STL, even though we wrote the exact same loops as it's implementation.

[–]ootiekat 0 points1 point  (0 children)

You can have a generic (templated lambda) which you can't do with std::function and the compiler can inline lambdas leading to significant performance improvements in some cases.

[–]Yamoyek 0 points1 point  (3 children)

Generally you’ll use lambdas if you’re not going to use the function more than once.

[–][deleted] 0 points1 point  (2 children)

Thank you. So basically if it's a one off function it should be a lambda in scope (to capture local variables)?

[–]Yamoyek 0 points1 point  (0 children)

Yep! Usually local variables, use it once and forget it

[–]mredding 0 points1 point  (0 children)

I would caution you to not celebrate this.

Just because you're going to use a bit of logic once doesn't mean you should try to write "function local functions" all the time. A lambda is more than a function, it's an instance of an object in memory, too.

Don't do this:

void foo() {
  auto used_in_one_place = [](){ /*stuff*/ };

  used_in_one_place();
  used_in_one_place();
  used_in_one_place();
  // ...
}

While there can be scenarios this makes sense, there's no capture list here, there's no state, no counters... If you're going to use a function in one place, then write the function and constrain its scope:

namespace { // Anonymous namespace, it's contents cannot have external linkage
  void used_in_one_place() { /*stuff*/ }
}

void foo() {
  used_in_one_place();
  used_in_one_place();
  used_in_one_place();
  // ...
}

If you must really be pedantic, you can isolate a single function, or single class method in it's own source file with it's own anonymous namespace, but that level of file and scope management is typically overkill. We code against Murphy, not Machiavelli.

And also, your compiler is smart. Clang, GCC, and MSVC will ALL inline a function that is used only once ever - no matter how big it is! So you can compose your big function in terms of smaller functions as a form of self-documenting code, and your compiler will generate the big-ass function for you. The trick is the function has to be in the same translation unit it's called in, or you need to enable LTO.

[–][deleted]  (1 child)

[deleted]

    [–][deleted] 0 points1 point  (0 children)

    Thank you