Learn how lambdas can make your code more efficient and easier to maintain.
Programming languages continually evolve, even those like C++ that established themselves long ago. They add functions to standard libraries and make other changes to ease the job of programmers working in a dynamic field.
As part of the updates brought about by the language’s evolution, C++ added support for lambda expressions in its 2011 release.
What exactly is a lambda expression, and how can you use it to your advantage as a C++ programmer?
What Is a Lambda Expression?
A lambda expression is also known as a lambda function. It’s an inline expression with the ability to accept arguments, carry out operations, and return a value, just like a typical function.
You can use a lambda inside another function and assign it to a variable. Lamba functions are a practical way of defining an anonymous function object in a program. Nearly every language supports lambdas, though each implementation varies from the others.
What Are the Parts of a C++ Lambda Expression?
Lambda expressions are easy to use in C++. You can break down the syntax of a lambda expression in C++ as follows:
[capture_clause](parameters) options { expression_body; }
For example:
int val = 13;
auto sumPlusVal = [val](int a, int b) mutable noexcept ->int { return val + a + b ; };
sumPlusVal(2, 5);
This code declares the sumPlusVal variable auto because a lambda expression can accept and return any data type. This leaves it up to the compiler to determine the type during compilation.
From the code above, you can see that a lambda expression contains several parts that specify how it operates. Here’s a quick overview of each of these components.
- Capture clause: This is the first part of a lambda expression where you can specify pre-existing variables or define new ones to use in the expression body. There are different ways to specify captures, for example:
auto addTwo = [foo](){ return foo + 2; };
auto addThree = [&bar](){ return bar + 3; };
auto addAllVal = [=](){ return foo + bar; };
auto addAllRef = [&](){ return foo + bar; };
auto createVarInCapture = [fooBar = foo + bar](){ return fooBar * 5; };
auto errorExpression = [](){ return foo + 2; }; - Parameters: This part of the lambda expression is also optional. It contains the function parameters required by the lambda. This isn’t any different from the usual way you’d define function parameters in C++.
- Options: You can also specify options when defining a lambda expression. Some options you could use are: mutable, exception (e.g noexcept in the first sample code), ->return_type (e.g ->int), requires, attributes, etc. The mutable option is often used because it allows captures to be modifiable inside the lambda. The code below demonstrates this.
int value = 10;
auto decrement = [value](){ return --value; };
auto increment = [value]() mutable { return ++value; };
increment();Although the other options are rarely used, you can get more information about them on the cppreference.com page on lambdas.
- Expression body: This is the lambda expression’s body which executes and returns a value, much like a function would. If necessary, you can split the body of a lambda expression across more than one line. However, it’s best practice to keep it as brief as possible to prevent disorganized code.
What Are the Benefits of Lambda Expressions?
There are many advantages to using lambda functions in your code. Apart from increased development speed and efficiency, the top benefits you receive from lambdas are as follows:
- Lambda expressions help keep code clean. One of the best ways to keep your code simple and neat is by using lambdas where possible. This can be very helpful in maintaining a readable and reusable code structure.
- You can pass lambdas to other functions as parameters. The C++ standard library’s std::sort() method makes use of this benefit. You can pass a lambda as one of this method’s parameters to specify how the function should carry out the sort. For instance:
std::vector<int> arr = {2, 5, 1, 3, 4};
std::sort(arr.begin(), arr.end(), [](int a, int b){ return a < b; }); - Lambdas are reusable. Sometimes, you may want to make a block of code reusable in the scope of a function in your program without having to define a new function. Lambdas can be very helpful in such cases. Consider the following example of a reusable lambda expression:
#include <iostream>
using namespace std;int main() {
auto addUp = [](auto a, auto b, auto c) noexcept {
cout << "Now adding up... " << a << ", " << b << " and " << c << endl;
return a + b + c ;
};cout << addUp(22, 33, 44) << endl;
cout << addUp(string("Happy "), string("Birth"), string("day")) << endl;
cout << addUp(true, false, true) << std::endl;
}This program produces the following result:
This example demonstrates how simple it is to define a lambda so that you can use it with any type.
Using Lambdas in C++
There are many other benefits that lambda expressions offer, and you’ll discover them as the structure of your program grows more complex. In fact, C++ programmers sometimes refer to lambda expressions as closures because they are such a great way to implement closures in code.
You should consider lambda expressions if you want to incorporate modern C++ concepts into your codebase.