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

Share the Post:
XDR solutions

The Benefits of Using XDR Solutions

Cybercriminals constantly adapt their strategies, developing newer, more powerful, and intelligent ways to attack your network. Since security professionals must innovate as well, more conventional endpoint detection solutions have evolved

AI is revolutionizing fraud detection

How AI is Revolutionizing Fraud Detection

Artificial intelligence – commonly known as AI – means a form of technology with multiple uses. As a result, it has become extremely valuable to a number of businesses across

AI innovation

Companies Leading AI Innovation in 2023

Artificial intelligence (AI) has been transforming industries and revolutionizing business operations. AI’s potential to enhance efficiency and productivity has become crucial to many businesses. As we move into 2023, several

data fivetran pricing

Fivetran Pricing Explained

One of the biggest trends of the 21st century is the massive surge in analytics. Analytics is the process of utilizing data to drive future decision-making. With so much of

kubernetes logging

Kubernetes Logging: What You Need to Know

Kubernetes from Google is one of the most popular open-source and free container management solutions made to make managing and deploying applications easier. It has a solid architecture that makes

ransomware cyber attack

Why Is Ransomware Such a Major Threat?

One of the most significant cyber threats faced by modern organizations is a ransomware attack. Ransomware attacks have grown in both sophistication and frequency over the past few years, forcing

data dictionary

Tools You Need to Make a Data Dictionary

Data dictionaries are crucial for organizations of all sizes that deal with large amounts of data. they are centralized repositories of all the data in organizations, including metadata such as