How to Use Lambda Expressions in C++11

by Steve Clamage and Darryl Gove

Lambda expressions provide the flexibility of function pointers, but with the benefit of improved extensibility through the capture of local variables. This article describes their syntax and shows how to use them.

Published July 2014


About the Authors
Darryl GoveDarryl Gove is a senior principal software engineer in the Oracle Solaris Studio team, working on optimizing applications and benchmarks for current and future processors. He is also the author of the books Multicore Application Programming, Solaris Application Programming, and The Developer's Edge. Find his blog at http://blogs.oracle.com/d.
Stephen ClamageSteve Clamage is a senior engineer in the Oracle Solaris Studio team and project lead for Oracle's C++ compiler. He has been the chair of the U.S. C++ Standards Committee since 1996.
See Also
- RAW pipeline hazards
- Oracle Solaris Studio
- SPARC Systems
- Studio Forums
- Tech Articles for Developers
Follow OTN
OTN Homepage

Introduction

Lambdas are probably one of the defining features of the recent C++11 standard. They are a way of treating functions as objects. Lambda expressions can be used in the same situations in which objects can be used—for example, they can be assigned to variables and passed as parameters—and like functions, they can also be evaluated.

Lambdas can be used where a function requires a function as one of its parameters. For example, the C qsort() function takes a pointer to a comparison function, as shown in Listing 1.

#include <stdlib.h>
#include <stdio.h>

static int intcompare(const void *p1, const void *p2)
{
  int i = *((int *)p1);
  int j = *((int *)p2);
  return (i < j) ;
}

int main()
{
  int a[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
  qsort((void *)a, 10, sizeof (int), intcompare);
  for (int i = 0; i < 10; i++) { printf("%d ", a[i]); }
  printf("\n");
  return 0;
}

Listing 1

There are a few things that make the code in Listing 1 a less-than-ideal solution:

  • The comparison function needs to be declared separately. This increases the risk that the wrong comparison function will be passed into the qsort() operation.
  • The comparison function takes void * parameters, so some degree of type checking has been lost.
  • The comparison function cannot see any locally scoped variables. So if there were additional factors that influenced the sort, they would have to be declared with a more global scope.

Listing 2 shows the example in Listing 1 recoded to use the C++ std::sort() algorithm with a lambda expression. Because std::sort() is a template, all type information is preserved. Notice how the lambda is written in the place where a function name would normally go.

#include <algorithm>
int main()
{
  int a[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
  std::sort( a, &a[10], [](int x, int y){ return x < y; } );
  for(int i=0; i<10; i++) { printf("%i ", a[i]); }
  printf("\n");
  return 0;
}

Listing 2

Basic Lambda Expression Syntax

At the core, a lambda expression is very similar to the declaration of a function. We can extract the lambda expression from Listing 2 and examine it in more detail:. The extracted lambda expression is shown in Listing 3:

[](int x, int y){ return x < y ; }

Listing 3

If we compare the lambda expression to a function, we can see that instead of a function name, it has a pair of empty square brackets, which are the capture expression. These brackets indicate that what follows is a lambda expression. They do not need to be empty; we will discuss their contents in a later section.

If the lambda body consists only of a return type, the type of the returned expression is implicitly the lambda return type. If you want to specify the return type explicitly, you use the new C++11 syntax for a trailing return type on a function declaration. For an ordinary function returning a type T, you can write the following:

auto foo(...) -> T { ... }

For a lambda, you would write this:

[] (...) -> T { ... }

The rest of the lambda expression looks like a regular C or C++ function body.

Passing a Lambda to a Function Pointer

The C++11 Standard Library has a template called function that can accept a function of the specified type or a lambda with a matching return type and parameter list. From that it produces a pointer to the function type, for example, Listing 4 could be used as the type of a function parameter that takes an int parameter and returns a void. You could pass it anything that looks like a matching function or lambda.

std::function<void(int)>

Listing 4

Listing 5 shows a function that scans an array and applies a given function to every element.

void scan( int* a, int length, std::function<void(int)> process )
{
  for(int i=0; i<length; i++) {
      process(a[i]);
  }
}

Listing 5

Listing 6 shows how the scan() function can be called passing either a function or a lambda expression as a parameter.

void f(int);
int a[10];
...
scan(a, 10, f);
scan(a, 10, [](int k)->void { ... } );

Listing 6

Variable Capture in Lambda Expressions

So far, we've treated a lambda expression rather like a standard function call; we pass parameters in and we get some result back. However, a lambda expression declared in a function body can also capture any local variables of that function that are visible at the point where the lambda is declared.

Suppose that we need to use function scan(), but we want the process function to operate only on values greater than some threshold. We can't modify scan(), and we can't get scan() to pass more than one argument to the process function. But if we pass a lambda expression into the scan() function, we can have it capture a local variable from its environment.

In Listing 7, we've placed the variable that we want to capture between the square brackets—in the capture expression. This effectively passes an additional parameter into the lambda expression, but it does not require a change to the definition of the scan function. Just like passing a parameter into a function, what we've effectively done is to capture a copy of the value threshold at the point where the function is called; this is known as capture by value.

#include <algorithm>
void scan( int* a, int length, std::function<void(int)> process)
{
  for(int i=0; i<length; i++) {
    process(a[i]);
  }
}
int main()
{
  int a[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
  int threshold = 5;
  scan(a, 10,
    [threshold](int v)
    { if (v>threshold) { printf("%i ", v); } }
  );
  printf("\n");
  return 0;
}

Listing 7

There is a shortcut, [=], which means "capture every variable by value." In Listing 8, we rewrite the function call to use this shorter expression.

scan(a, 10, [=](int v) { if (v>threshold) { printf("%i ", v); } });

Listing 8

Note: Capturing a variable by value means making a local copy. If there are many local variables, capturing all of them could impose a noticeable overhead on the lambda.

However, there are some instances where we want to modify a captured variable. For example, suppose we want to compute the maximum value and store it in the variable max. In this case, we do not want to use a copy of the value of the variable; we want to use a reference to the variable—so that we can modify the variable in the template. This is called capturing variables by reference. An example of this is shown in Listing 9.

#include <algorithm>
void scan(int * a, int length, std::function<void (int)> func)
{
  for(int i=0; i<length; i++) {
    func(a[i]);
  }
}

int main()
{
  int a[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
  int threshold = 5;
  int max =0;
  std::sort( a, &a[10], [](int x, int y){return (x < y);});
  scan(a, 10,
   [threshold,&max](int v) { if (v>max) {max = v;}
                     if (v>threshold) { printf("%i ", v); } });
  printf("\n");
  printf("Max = %i\n",max);
  return 0;
}

Listing 9

Again, there is a shortcut, [&], for the situation where every variable should be captured by reference.

Lambda Expressions, Function Objects, and Functors

Although lambda expressions are a new feature of C++11, they are really a convenient way of accessing existing language features. A lambda expression is a shorthand notation for a function object. A function object is an object of a class type that has a member operator()()—the function-call operator—and can, therefore, be called like a function. The function object type is called a functor. Listing 10 shows an example of a functor.

class compare_ints {
public:
  compare_ints(int j, int k ) : l(j), r(k) { }
  bool operator()() { return l < r; }
private:
  int l, r;
};

Listing 10

You can create a compare_ints object initialized with two integer values and use the function-call operator to return true if the first value is less than the second:

compare_ints comp(j, k);
bool less_than = comp();

Or you could create a temporary object on the fly and use it directly:

bool less_than = compare_ints(j, k)();

Using a lambda expression gets that effect without having to create and name the functor class. The compiler creates an anonymous functor for you, as shown in Listing 11.

auto comp = [](int j, int k) { return j < k; };
bool less_than =  comp(l,r);

Listing 11

In Listing 11, comp is an object of the anonymous functor type.

You can also do the operation on the fly:

bool less_than = [l,r]() { return l < r; }();

Conclusion

Lambda expressions are a very powerful extension to C++. They provide the flexibility of function pointers, but with the benefit of improved extensibility through the capture of local variables.

Obviously lambda expressions become even more useful when combined with the extensive template features found in C++, and you will often encounter them in code written to the C++11 standard.

See Also

Revision 1.0, 04/11/2014

Follow us:
Blog | Facebook | Twitter | YouTube