top of page
Writer's pictureAniket

Lambda in C++11/14/17 - Short yet powerful!




A lambda expression is often ignored, but a powerful mechanism in C++. You can use a lambda in C++ from C++11 through C++17, with each version of C++ making it more flexible in syntax and semantics. In this blog article, we will take a look at lambda syntax, semantics and their use cases.


What is lambda?

A lambda expression in C++ is an unnamed function, just like lambdas in python or any of the newer languages. It is often short and one-liner function, though that is not the condition to define and use it. Lambdas can be used in a variety of situations, and often find their place for predicates or functors (objects acting as a function pointer).


The syntax of Lambda:

The basic syntax of lambda is something as below:


[<capture_context>] mutable throw() -> <return_type> (<input_arguments>)

{

<lambda_body>

}


Example:

int x = 5;

auto AddFive = [=] (int y) { return x+y; }

std::cout << AddFive(4) << std::endl; // Will print 9


Here are parts of lambda which you need to be aware:


<capture_context>

Tells compiler which variables to make available from surrounding scope where lambda is defined. In the above example, the variable ‘x’ is made available inside lambda with ‘[=]’ syntax, which also means capture-by-value.


You can use ‘[&]’ to capture all variables from surrounding scope by reference. Default, it will capture everything by value, and you can then leave capture syntax with empty brackets like ‘[],’ to indicate the default capture. Optionally, each variable can be specified with different capture syntax. E.g.,


int x, y;

[=] Captures both x and y by value

[&] Captures both x and y by reference

[ ] Captures both x and y by value

[=x,&] Captures x by value and y by reference

[y, &] Captures x by reference and y by value

[x,y] Captures x and y by value

[&x,&y] Captures x and y by value


<return_type>

If you use a lambda in C++11 compiler, then this parameter is needed. If your lambda function body is a single liner, then the compiler will try to deduce the return type for you, but if not (e.g., multiple returns are present, and they don’t match) then the compiler will make you specify this in C++11.


The situation changes, however, in C++14 onwards. You can now specify ‘auto’ return type, and C++17 makes specifying return type completely optional for all cases. C++17 kept return type specification for backward compatibility with older programs which may get upgraded to run with C++17 compiler over time.


<input_arguments>

These are input arguments to lambda function, and like any other function, are always optional.


<lambda_body>

This is the function body for lambda function and can contain any valid C++ expression here. It can also be a multi-liner and contain complex logic here (though keep in mind that, that is not how typically lambdas are used). The return type is also optional, and lambda can contain void return type.


Keywords ‘mutable’ and ‘throw’

By default, when you capture any variable by value, then it is immutable, and as the name indicates, ‘mutable’ keyword makes it modifiable inside the lambda body. So it means if you don’t use ‘mutable’ keyword and try to modify then the compiler will throw the error. C++14 onward this is optional though. Also, keep in mind that even if you can modify capture by value variable inside the lambda function body with a mutable keyword, you will be modifying variable copy and not the original variable.


‘throw’ keyword is exception specification. Like any other C++ function, you can specify exceptions which your lambda function may throw. You can also specify empty exception specification with ‘throw()’ which would make your lambda a no-throw function. Like any other C++ function, specifying exception specification is not really encouraged unless you are sure that code can guarantee it in all cases and conditions run-time.


Why use lambda?

It will be an obvious question when one learns about lambdas first time is to why use it? What makes them so special? A couple of reasons to use them:


  1. Lambdas are short, in-place or inline function definitions, which appear at the place where they are called (or are located very near to call place). This reduced the mental stack overhead while reading the code.

  2. Most compilers will optimize lambda calls on similar lines of inline functions. Comparatively, named function calls incur more calling cost. This should not be a sole motivation for using them, but something to keep in mind.

  3. Lambdas for predicate and functors are easier to write and take much lesser code compared to full-fledged template functions or classes which act like functors. It also makes the code easier to understand, and that on itself has huge maintenance benefit. We will see an example of this in a subsequent section.

  4. Lambdas enable higher order functors which can return another functor. This can be achieved outside lambdas too, though the amount of code needed to achieve higher order lambdas is much smaller and much easier to understand. Again, we will see an example of this.


Lambda-as-a-predicate

When lambda is used as a predicate, it works as functors for most C++ STL algorithms. The below example highlights the simple addition use case. We do the same sum of all vector elements, once with custom class acting as a predicate, and the second time with lambda. This is a very good example which highlights how lambda can make your code shorter and crisp.


Output

Adding all numbers ...

Total=165

Total (with lambda by reference capture)=165


Some people may argue for the above example that it is better to write a custom class which is reusable across places (like ‘Sum’ template above) than using lambda. But two things there, first you can store lambda in a function object (or auto variable) and call it anywhere later in the code so achieving reusability concern. Secondly, it is very likely you don’t need such predicate classes in more than one block or module of code so often this concern is not of much value.


Higher-order and nested lambdas

Higher order functions are those who take as input and/or return from its body other function objects (or functors). Lambdas are an essentially a special version of C++ functions, so you can define higher-order lambdas too. Here is one example of this.



Output

Even numbers: 22 44

Odd numbers: 11 33 55

Nested lambda: TimesTwoPlusThree(5) = 13

HigherOrderMultiplier(AddTwoIntegers(5), 4) = (5+4)*2 = 18


This code is self-explanatory. Return type ‘-> function<int(int)>’ may seem odd but it is returning functors which take and returns an integer. Do notice how ‘AddTwoIntegers,’ which is a lambda, returns another lambda from its body. Later, we send an instance of that returned functor to ‘HigherOrderMultiplier’ which is lambda too.


Such higher-order lambdas allow us to capture internal class or function state and store it someplace to use it asynchronously in some other part of code later. If used smartly, this is a very powerful technique, as you can capture state as well as any optional computation around it and return it to use it in other parts of the code.


This above example also highlights the case where lambda is used inside the template. You can also use lambda inside classes or class templates. If you do use lambda inside the class, then you have to capture ‘this’ pointer by value (or do default capture by value for everything). Once you do that, you can use any class member variables by reference in your lambda body without any issue, and without capturing them explicitly by reference.


Summary

Here’s a summary of lambdas:

  • Lambdas are unnamed functions in C++ which have specific syntax, and which allow you to write inline code where functors or function objects would be accepted.

  • Lambdas capture variables from enclosing scope (where they are defined) either by value or by reference and make them available to use inside the body of the lambda.

  • Lambdas can be higher-order with taking input or returning another lambda from its body. Lambdas can be used as part of classes and templates.

  • In C++14 and C++17 return type of the lambda is auto deducted.

  • Lambdas make code short and easier to understand.


88 views0 comments

コメント

5つ星のうち0と評価されています。
まだ評価がありません

評価を追加
bottom of page