Channels ▼
RSS

C/C++

Lambdas in C++11


Consider an application that performs a test of divisibility on all the int values of the numbers vector introduced in the previous examples. You can use a for_each with a lambda expression to check whether each number is divisible by another number leaving no remainder. Because the user will specify different values for the divisor, you want to declare it as an int outside of the lambda expression, but you want to capture it by value to make it available inside of the lambda body. The following lines show an example that specifies the variable name (divisor) that you want to capture by value within the lambda capture clause. Thus, the lambda starts with [divisor]. The example doesn't include the necessary code to change the value for the divisor variable, so that I can keep the focus on the lambda capture syntax.

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main()
{
    // The user would introduce different values for divisor	
    int divisor = 3;
    vector<int> numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 };
    for_each(numbers.begin(), numbers.end(), [divisor] (int y)
    {
        if (y % divisor == 0)
        {
            cout << y << endl;
        }
    });
}

You can also achieve the same goal by using the [=] capture option, which captures any referenced variable within the lambda by value (making a copy). This option captures only the outside variables referenced within the lambda body. In this case, the only referenced variable is divisor, so the following lines are equivalent to the previous for_each lines.

for_each(numbers.begin(), numbers.end(), [=] (int y)
{
	if (y % divisor == 0)
	{
		cout << y << endl;
	}
});

The following lines show a different version of the previous example, which computes the sum of all the numbers that are divisible by the value specified in divisor. In this case, the lambda starts with [divisor, &sum]. Thus, the lambda captures divisor by value and sum by reference because sum has an ampersand (&) prefix. If the int value is divisible by divisor leaving no remainder, the value is added to the sum variable captured by reference.

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

int main()
{
    int sum = 0;
    int divisor = 3;
    vector<int> numbers { 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 };
    for_each(numbers.begin(), numbers.end(), [divisor, &sum] (int y)
    {
        if (y % divisor == 0)
        {
            cout << y << endl;
            sum += y;
        }
    });

    cout << sum << endl;
}

As in the previous example, you can achieve the same variable-capture goals by using different combinations of capture options. The following list includes different capture options that produce the same result as [divisor, &sum] in the previous example.

  • [=, &sum]: Captures any referenced variable within the lambda by value (making a copy), except sum that has to be captured by reference.
  • [&, divisor]: Captures any referenced variable within the lambda by reference, except divisor that has to be captured by value.

You can use either = or & as a default option combined with the exceptions. For example, in a case where you have a more complex lambda that captures many variables by value but has to capture sum, division, and multiplication by reference, you might specify the following capture options: [=, &sum, &division, &multiplication]. So, you can specify many exceptions that follow the default option. Another important capture option is [this] because it allows you to capture the this pointer of the enclosing class.

Passing Lambda Expressions with std::function

Each time you are creating a lambda expression, its implementation creates a new class under the hood. Thus, each lambda creates a separate class and has a different type. As you might guess, even when many lambda expressions receive the same arguments and return the same type, each of them will be a different class; that is, a different type. Luckily, C++11 incorporates a wrapper for any of the following functions with std::function and makes it easy to pass around lambda expressions:

  • Lambda expression
  • Function pointer
  • Functor

The following lines show a simple example that uses std::function to create the run_within_for_each function, which receives the function to be executed within a for_each as an argument. Notice that it is necessary to include the functional header.

#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>
using namespace std;

void run_within_for_each(std::function<void (int)> func)
{
    vector<int> numbers{ 1, 2, 3, 4, 5, 10, 15, 20, 25, 35, 45, 50 };

    for_each(numbers.begin(), numbers.end(), func);
}

int main()
{
	auto func1 = [](int y)
	{
		cout << y << endl;
	};

	auto func2 = [](int z)
	{
		cout << z * 2 << endl;
	};

	run_within_for_each(func1);
	run_within_for_each(func2);
}

The run_within_for_each function receives a std::function<void (int)>, so it is possible to call this function with a lambda expression that returns void and receives an int as an argument. The function declares the same vector<int> used in the other examples and executes a for_each with the received std::function<void (int)> as the function to be executed for each int value. The main method declares two different lambda expressions that receive an int argument and make one call to the run_within_for_each with each of these lambda expressions. In case you want to pass around a lambda expression that receives two int arguments and returns a bool value, you can use std::function<bool (int, int)>.

As you might guess, it is also possible to use std::function to specify the return type for any function. For example, the following function returns a std::function<bool (int)>. The code returns a lambda expression that receives an int as a parameter and returns a bool value, wrapped in std::function<bool (int)>:

std::function<bool(int)> create_function()
{
	return [](int x)
	{
		return (x < 100);
	};
}

The combination of lambda expressions with the std::function can boost your productivity and reduce a huge amount of boilerplate code when you work with code that requires the use of functors.

Conclusion

I've provided some very simple examples that allowed me to focus on the new syntax introduced by C++ lambda expressions. You can apply all the things learned in these examples in more-complex scenarios, such as when you call STL methods or work with libraries that require functors that can be replaced with lambdas, as happens with Threading Building Blocks. I suspect you'll find that once you start using C++11 lambdas, there is no going back!


Gastón Hillar is a senior contributing editor at Dr. Dobb's.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video