Lambda Functions Are Ready for Prime Time

Lambda Functions Are Ready for Prime Time

our years ago, I presented a pioneer proposal for adding lambda expressions to standards C++. The lambda proposal has since undergone significant revisions and enhancements. In their new form, C++0x lambda expressions and closures enable you to write unnamed function definitions at the place of call in a concise and convenient manner. The following sections will present the new syntax of C++0x lambda expressions.

You need a nameless function defined at the place of call. In C++03, the only way to do that is to define a function object manually. Alas, this is tedious and not well-suited for creating a one-time function object used “on the fly.”

Use the new C++0x lambda function syntax to create a nameless local function conveniently.

Presenting The Problem

Suppose you want to find the first employee whose salary is within a specified range from a sequence of employees. The find_if algorithm comes in handy:

double min_salary=1000;std::find_if(emps.begin(), emps.end(),             within(min_salary, 1.5*min_salary));

Your attention is drawn to the third argument of find_if which is a function object of type within. Defining this class is laborious, as the following code demonstrates:

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

Although within is used only once, there's no escape from writing this full-blown class with its data members, constructor, overloaded operator() and finally, instantiating it in the argument list of the find_if call above. You certainly don't want to repeat this tedium every time you use an algorithm! Why not let the compiler generate the code for the function class automatically? That's exactly what lambda expressions are all about.

Due Closure

Using the new lambda syntax, you would rewrite the above find_if call like this:

double min_salary = 1000;double upper_lmt = 1.5*min_salary;std::find if(emps.begin(), emps.end(),   [&](const employee& emp)     (emp.salary() >= min_salary && emp.salary()< upper_lmt));

The last two lines contain a lambda expression which you'll explore in detail in a moment. Note that the manually defined function class is gone. Instead, the compiler implicitly generates a similar function class and replaces the user-written lambda expression with an unnamed function object of the generated class. The implicitly instantiated function object is called a closure.

Introducing Lambda

Let's look at the lambda expression itself. All lambda expressions begin with the lambda introducer [] (I'll explain the meaning of the & between the brackets later). The lambda's parameter list appears after the introducer. As with regular functions, the parameters are enclosed within parentheses. Finally, the last line contains the lambda expression's body:

(emp.salary() >= min_salary && emp.salary()< upper_lmt)

Here, the body contains only one statement. Because that evaluates to a Boolean expression, the compiler automatically deduces the lambda's return type as bool; however, you may declare the return type explicitly if you like, as shown below:

[&](const employee& emp)->bool //explicit return type (emp.salary() >= min_salary && emp.salary()< upper_lmt));

When the body of a lambda expression contains multiple statements, you have to enclose the body in braces and add an explicit return type:

//lambda body with multiple statements[](int x, int y)->int { int z; z = x + (y*2); return z; }

Let's analyze the last example:

  • [] is the introducer.
  • The parameter list declares x and y as int.
  • The explicit return type int is specified next.
  • Finally, the body, which contains three statements, is enclosed in braces.

Notice that the return keyword is mandatory when the body consists of multiple statements.

External References

Roughly speaking, lambda expressions are divided into two categories: those with external references and those with no external references. What it's all about? Lambda expressions with external references access variables defined outside the lambda expression's parameter list. By contrast, lambda expressions with no external references don't 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; } 

And here's one with external references:

//Note: pseudo C++ codeint z;myfunc([](int x, int y)->int {return x + y + z;}); 

The last lambda accesses the variable z which is declared outside the lambda expression. In cases like this, the referenced external variable must be represented in the closure somehow. There are two ways in which C++ can store variables with external references in the closure:

  • Store a copy of z in a data member.
  • Define a reference data member and bind it to z.

You instruct the compiler which type of representation you want?copy or reference?by providing a lambda capture. A lambda capture is a description of the environment that the lambda expression will access, where the environment is the closure's data members. The capture appears between the brackets of the introducer. There are several types of captures but I'll demonstrate only two of them:

  • The default capture [=] indicates that, by default, closure copies the values of the external variables into its private data members.
  • The default capture [&] indicates that, by default, closure contains reference variables bound to the external variables.

Following this discussion, you now know that the correct syntax for the last lambda expression is either:

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


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

Depending on whether you want the closure to store a copy of z or a reference to it, respectively.

Getting back to the original example, you can tell what the role of the [&] in the following lambda is:

double min_salary = 1000;double upper_lmt = 1.5 * min_salary;std::find if(emps.begin(), emps.end(),[&](const employee& emp)  (emp.salary() >= min_salary && emp.salary()&lr; upper_lmt));

The body refers to the variables min_salary and upper_lmt, which are declared independently of the lambda expression. The lambda introducer contains the & capture which instructs the compiler to create reference data members that are bound to min_salary and upper_lmt. As you can see, lambda functions are likely to simplify your code.

At the time of writing, several compilers including Visual C++ 10 (beta) and Intel C++ 11.0 already support lambdas. Others are soon likely to follow suit


About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist