Lambdas are a recent addition to C++ and are also included in other languages. Think of them like unnamed methods but with some extra concepts that you need to know. Or if not, you’ll be even more lost than with function pointers.
The C++ language has three ways to call code and other languages will be similar. What we’re mainly talking about here is not executing a method directly but instead passing a method to some other code that will run the method for you at some time either right away or in the future.
- The first is to just call a method directly. Method names are pointers and you can pass them to other methods to be called for you. This way also includes using function pointers.
- The second is to write a function object. This is a class that implements the function call operator. You can pass an instance of a function object class to another method to be called for you.
- And the third is to use a lambda expression which will create a temporary unnamed function object behind the scenes. This unnamed function object is called a closure.
When I explained function objects, I said that one of their advantages over a regular method is the ability of the function object class to declare member data. Just like any data variables, this member data can be initialized by copying a value from someplace else or by referring or referencing someplace else. This is critical to understanding lambdas because they highlight this ability and make it really easy to specify what variables will be captured by value and which will be captured by reference.
But what do I mean when I say that a lambda captures a variable? Let’s say that you have a method with a local variable called message. This is a string that you want printed only for items that match some criteria. You have the items in a collection and want to go through each item, test if it meets the criteria, and if so, then print the message. Seems simple, right? You could write all this code just like I said. Or you can use any of several utility methods that will handle most of the code for you. All you need to do is provide some small piece of your own code that will be run for each item in the collection. You don’t need to worry about navigating through the collection anymore. All you need to do is write a small piece of code that receives one item at a time from the collection and then you can do whatever you want with the item.
This is a perfect example of where a lambda can really help. You don’t run the lambda expression yourself. You just need a way to quickly and easily write this piece of code that the utility method will run for you. The question then is how will you get the message text into the lambda? You can’t pass the string message when the lambda is called because it’s not your code that’s calling the lambda. You need to get the message preloaded into the lambda. You can do this with a function object by passing the message to a function object constructor. Instead, all lambdas start out with a lambda capture block that consists of a pair of square brackets. Inside these square brackets, you can list individual variables that the lambda will have access to. These variables get captured by the unnamed function object closure. That’s just a lambda way of saying that the function object will get initialized with either copies or references to the variables.