Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Lambda Expressions and Closures, Part I

Last updated Jan 1, 2003.

The C++ standards committee approved the lambda expressions proposal at the Bellevue meeting of February 2008. The latest version of the proposal is very different from the initial draft that I presented here in 2005. The terminology has changed, as have the syntax, semantics, usage and underlying implementation of lambda expressions. In the first part of this series I introduce the fundamental concepts of the latest lambda proposal.

Why Use Lambda Expressions?

A lambda expression (also known as a lambda function) is nameless function defined at the place of call. As such, it's similar to a function object. Indeed, lambda expressions are automatically transformed into function objects. So why not use function objects in the first place? As useful it may be, creating a function object is a laborious task: you have to define a class with data members, an overloaded function call operator and a constructor. You then have to instantiate an object of that type at the place of all. This is very verbose and ånot well-suited for creating a one-time function object that is used "on the fly".

To demonstrate the usefulness of lambda expressions, suppose you need to find the first employee whose salary is within a given range. Using traditional and verbose function objects, you could first write a function class called withinrange:

class withinrange { 
 double low, high;
public:
 withinrange(double l, double h) : low(l), high(h) { }
 bool operator()(const employee& emp) { 
  return emp.salary() >= low && emp.salary() < high;

  }
};

Next, you use the find_if algorithm to locate the first employee whose salary is within the specified range from a sequence of employees:

double minimum_salary=1000;
std::find if(employees.begin(), employees.end(),
withinrange(minimum_salary, 1.25*  minimum_salary));

The third argument of the find_if call is a function object that in other programming languages is called a closure. A closure is an unnamed function object that stores the said function's environment. The environments consists of local variables that the function accesses. In this case, the data members low and high are the environment stored in the closure. In simpler words, a closure is the hypothetical function object that the compiler will generate for a given lambda expression.

Lambda Expression Usage

Using the new lambda proposal, the above find_if call can be rewritten as:

double minimum_salary = 1000;
double upper_limit = 1.25 * minimum_salary;
std::find if(employees.begin(), employees.end(),
[&](const employee& emp)  (emp.salary() >= minimum_salary && emp.salary() < upper_limit));

First, notice that the lambda expression is self-contained within a single line of code. You don't need a separate function class anymore.

A lambda expression begins with the lambda introducer [] (I will discuss the meaning of the & between the brackets in a different part of this series). The lambda expression's parameter list appears after the lambda introducer. In this example, the parameter list consists of the sole parameter const employee&. This entire lambda expression is said to be monomorphic because the types of its parameters are explicitly specified. Here, the type of the sole parameter emp is const employee&. A polymorphic version of the same expression would be:

[&](emp) (emp.salary() >= minimum_salary && emp.salary() < upper_limit)

The latter form requires that the parameter types shall be deduced from the context (the place of call). The current proposal focuses only on monomorphic lambda expressions so I will not discuss polymorphic lambdas in this series.

Implicit and Explicit Return Types

The last part of the previous lambda expression:

(emp.salary() >= minimum_salary && emp.salary() < upper_limit)

is the lambda expression's body. A lambda expression's body can consist of a single parenthesized expression. In that case, the return type of the lambda function is implicitly deduced from the expression itself. For example, the following expression yields a bool result:

(emp.salary() >= minimum_salary && emp.salary() < upper_limit)

Therefore, the return type of the above lambda expression is bool.

Technically speaking, if the return type isn't explicitly specified in a lambda expression, it's defined as decltype(e) where e is the body of the lambda expression.

You may specify the return type of a lambda expression explicitly, though. Using the new function declaration syntax, here's how you do it:

[&](emp) ->bool (emp.salary() >= minimum_salary && emp.salary() < upper_limit)

Lambda Expression Body

A lambda expression's body may contain more than one statement. In such cases, the entire body is enclosed within a pair of braces and must have an explicit return statement. The following lambda expression takes two parameters of type int and has a return type int. Its body consists of three statements enclosed in a {} block:

[](int x, int y) -> int { int z; z = x + y; return z; }

The return statement in this example is mandatory because the lambda expression's body consists of multiple statements.  Likewise, the explicit return type after the parameter list is mandatory as well.

External References

Lambda expressions are divided into two major categories: lambda expressions with no external references and those with external references. The latter access variables that are defined outside the lambda expression's parameter list, as opposed to lambda expressions that do not access variables defined outside the lambda expression's parameter list.  Here's a lambda expression with no external references:

[](int x, int y) -> int { return x + y; }

Here's one with external references:

int z;
myfunc([](int x, int y) -> int { return x + y + z; } );//pseudo code

References to local variables declared outside of the lambda function bodies have been debated for a long time. The problem is that any local variable referenced in a lambda function body, e.g., z in the previous example, must somehow be stored in the resulting closure. How these variables are exactly stored in the closure is the disputed issue. Some proposed that copies of the external variables shall be stored in the closure. Copying however can be inefficient in some cases and might also lead to slicing and iterator invalidation. The other solution was to store references to the external variables in the closure. This approach can also be problematic as it could lead to dangling references. In the second part of this series I will show how the latest proposal solved the external references problem and how external references are represented in a closure.