Categories
c++ c++-faq c++11 lambda

What is a lambda expression in C++11?

1707

What is a lambda expression in C++11? When would I use one? What class of problem do they solve that wasn’t possible prior to their introduction?

A few examples, and use cases would be useful.

2

  • 24

    I’ve seen a case where the lambda was very useful: A colleague of me was doing code that has millions of iterations to solve a space optimization problem. The algorithm was much more speedy when using a lambda than a proper function! The compiler is Visual C++ 2013.

    – sergiol

    Feb 12, 2017 at 20:03

  • 1

    Here is another really good reference which explains very well what are lambda expressions in C++: Microsoft.com: Lambda expressions in C++. I especially like how well it explains the parts of a lambda expression, in particular: the capture clause, parameter list, trailing-return-type, and lambda body.

    Nov 26, 2021 at 16:03


1726

The problem

C++ includes useful generic functions like std::for_each and std::transform, which can be very handy. Unfortunately they can also be quite cumbersome to use, particularly if the functor you would like to apply is unique to the particular function.

#include <algorithm>
#include <vector>

namespace {
  struct f {
    void operator()(int) {
      // do something
    }
  };
}

void func(std::vector<int>& v) {
  f f;
  std::for_each(v.begin(), v.end(), f);
}

If you only use f once and in that specific place it seems overkill to be writing a whole class just to do something trivial and one off.

In C++03 you might be tempted to write something like the following, to keep the functor local:

void func2(std::vector<int>& v) {
  struct {
    void operator()(int) {
       // do something
    }
  } f;
  std::for_each(v.begin(), v.end(), f);
}

however this is not allowed, f cannot be passed to a template function in C++03.

The new solution

C++11 introduces lambdas allow you to write an inline, anonymous functor to replace the struct f. For small simple examples this can be cleaner to read (it keeps everything in one place) and potentially simpler to maintain, for example in the simplest form:

void func3(std::vector<int>& v) {
  std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

Lambda functions are just syntactic sugar for anonymous functors.

Return types

In simple cases the return type of the lambda is deduced for you, e.g.:

void func4(std::vector<double>& v) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [](double d) { return d < 0.00001 ? 0 : d; }
                 );
}

however when you start to write more complex lambdas you will quickly encounter cases where the return type cannot be deduced by the compiler, e.g.:

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

To resolve this you are allowed to explicitly specify a return type for a lambda function, using -> T:

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) -> double {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

“Capturing” variables

So far we’ve not used anything other than what was passed to the lambda within it, but we can also use other variables, within the lambda. If you want to access other variables you can use the capture clause (the [] of the expression), which has so far been unused in these examples, e.g.:

void func5(std::vector<double>& v, const double& epsilon) {
    std::transform(v.begin(), v.end(), v.begin(),
        [epsilon](double d) -> double {
            if (d < epsilon) {
                return 0;
            } else {
                return d;
            }
        });
}

You can capture by both reference and value, which you can specify using & and = respectively:

  • [&epsilon, zeta] captures epsilon by reference and zeta by value
  • [&] captures all variables used in the lambda by reference
  • [=] captures all variables used in the lambda by value
  • [&, epsilon] captures all variables used in the lambda by reference but captures epsilon by value
  • [=, &epsilon] captures all variables used in the lambda by value but captures epsilon by reference

The generated operator() is const by default, with the implication that captures will be const when you access them by default. This has the effect that each call with the same input would produce the same result, however you can mark the lambda as mutable to request that the operator() that is produced is not const.

18

  • 10

    @Yakk you have been trapped. lambdas without a capture have an implicit conversion to function type pointers. the conversion function is const always…

    Mar 31, 2013 at 22:17

  • 3

    Interesting – I originally thought that lambdas were anonymous functions rather than functors, and was confused about how captures worked.

    Mar 9, 2014 at 1:39


  • 72

    If you want to use lambdas as variables in your program, you can use: std::function<double(int, bool)> f = [](int a, bool b) -> double { ... }; But usually, we let the compiler deduce the type: auto f = [](int a, bool b) -> double { ... }; (and don’t forget to #include <functional>)

    Apr 10, 2015 at 16:15


  • 24

    I suppose not everyone understands why return d < 0.00001 ? 0 : d; is guaranteed to return double, when one of the operands is an integer constant (it is because of an implicit promotion rule of the ?: operator where the 2nd and 3rd operand are balanced against each other through the usual arithmetic conversions no matter which one that gets picked). Changing to 0.0 : d would perhaps make the example easier to understand.

    – Lundin

    Dec 17, 2015 at 7:32


  • 4

    @MohammadMamunHossain use std::array array instead of raw arrays and then it becomes trivial. (Which is good advice in most cases in C++ anyway)

    – Flexo

    Feb 26, 2018 at 8:27


929

What is a lambda function?

The C++ concept of a lambda function originates in the lambda calculus and functional programming. A lambda is an unnamed function that is useful (in actual programming, not theory) for short snippets of code that are impossible to reuse and are not worth naming.

In C++ a lambda function is defined like this

[]() { } // barebone lambda

or in all its glory

[]() mutable -> T { } // T is the return type, still lacking throw()

[] is the capture list, () the argument list and {} the function body.

The capture list

The capture list defines what from the outside of the lambda should be available inside the function body and how.
It can be either:

  1. a value: [x]
  2. a reference [&x]
  3. any variable currently in scope by reference [&]
  4. same as 3, but by value [=]

You can mix any of the above in a comma separated list [x, &y].

The argument list

The argument list is the same as in any other C++ function.

The function body

The code that will be executed when the lambda is actually called.

Return type deduction

If a lambda has only one return statement, the return type can be omitted and has the implicit type of decltype(return_statement).

Mutable

If a lambda is marked mutable (e.g. []() mutable { }) it is allowed to mutate the values that have been captured by value.

Use cases

The library defined by the ISO standard benefits heavily from lambdas and raises the usability several bars as now users don’t have to clutter their code with small functors in some accessible scope.

C++14

In C++14 lambdas have been extended by various proposals.

Initialized Lambda Captures

An element of the capture list can now be initialized with =. This allows renaming of variables and to capture by moving. An example taken from the standard:

int x = 4;
auto y = [&r = x, x = x+1]()->int {
            r += 2;
            return x+2;
         }();  // Updates ::x to 6, and initializes y to 7.

and one taken from Wikipedia showing how to capture with std::move:

auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};

Generic Lambdas

Lambdas can now be generic (auto would be equivalent to T here if
T were a type template argument somewhere in the surrounding scope):

auto lambda = [](auto x, auto y) {return x + y;};

Improved Return Type Deduction

C++14 allows deduced return types for every function and does not restrict it to functions of the form return expression;. This is also extended to lambdas.

4

  • 4

    In your example for initialized lambda captures above, why do you end the lamba function with the ();? This appears like [](){}(); instead of [](){};. Also shouldn’t the value of x be 5?

    Jun 9, 2016 at 13:25


  • 9

    @RamakrishnanKannan: 1) the () are there to call the lambda right after defining it and give y its return value. The variable y is an integer, not the lambda. 2) No, x=5 is local to the lambda (a capture by value which just happens to have the same name as the outer scope variable x), and then x+2 = 5+2 is returned. The reassignment of the outer variable x happens through the reference r: r = &x; r += 2;, but this happens to the original value of 4.

    – The Vee

    Jul 14, 2016 at 13:40

  • hey, by saying any variable currently in scope, what does it mean? it means capture all global variables globally and any local variables in this function?

    – http8086

    Dec 6, 2020 at 12:58

  • I saw in the documentation that a Throw has been added: docs.microsoft.com/en-us/cpp/cpp/…

    – Sxubach

    Jun 23 at 5:22

175

Lambda expressions are typically used to encapsulate algorithms so that they can be passed to another function. However, it is possible to execute a lambda immediately upon definition:

[&](){ ...your code... }(); // immediately executed lambda expression

is functionally equivalent to

{ ...your code... } // simple code block

This makes lambda expressions a powerful tool for refactoring complex functions. You start by wrapping a code section in a lambda function as shown above. The process of explicit parameterization can then be performed gradually with intermediate testing after each step. Once you have the code-block fully parameterized (as demonstrated by the removal of the &), you can move the code to an external location and make it a normal function.

Similarly, you can use lambda expressions to initialize variables based on the result of an algorithm

int a = []( int b ){ int r=1; while (b>0) r*=b--; return r; }(5); // 5!

As a way of partitioning your program logic, you might even find it useful to pass a lambda expression as an argument to another lambda expression…

[&]( std::function<void()> algorithm ) // wrapper section
   {
   ...your wrapper code...
   algorithm();
   ...your wrapper code...
   }
([&]() // algorithm section
   {
   ...your algorithm code...
   });

Lambda expressions also let you create named nested functions, which can be a convenient way of avoiding duplicate logic. Using named lambdas also tends to be a little easier on the eyes (compared to anonymous inline lambdas) when passing a non-trivial function as a parameter to another function. Note: don’t forget the semicolon after the closing curly brace.

auto algorithm = [&]( double x, double m, double b ) -> double
   {
   return m*x+b;
   };

int a=algorithm(1,2,3), b=algorithm(4,5,6);

If subsequent profiling reveals significant initialization overhead for the function object, you might choose to rewrite this as a normal function.

15

  • 11

    Have you realized that this question was asked 1.5 years ago and that the last activity was almost 1 year ago? Anyway, you’re contributing some interesting ideas I haven’t seen before!

    – Piotr99

    Mar 1, 2013 at 8:32

  • 8

    Thanks for the simultaneous define-and-execute tip! I think it’s worth noting that that works in as a contidion for if statements: if ([i]{ for (char j : i) if (!isspace(j)) return false ; return true ; }()) // i is all whitespace, assuming i is an std::string

    Mar 2, 2013 at 1:13


  • 79

    So the following is a legal expression: [](){}();.

    Apr 13, 2013 at 22:35

  • 8

    Ugh! Python’s (lambda: None)() syntax is so much more legible.

    – dan04

    May 30, 2013 at 3:28

  • 9

    @nobar – you’re right, I mistyped. This is legal (I tested it this time) main() {{{{((([](){{}}())));}}}}

    May 2, 2014 at 16:05