1
The C++11 standard has added hi(lambda function) em(lambda functions) to
2
bf(C++). As we've seen generic algorithms often accept an argument that can
3
either be a function object or it can be a plain function. Examples are the
4
tt(sort) and tt(find_if) generic algorithms. When the function called by the
5
generic algorithm must remember its state a function object is appropriate,
6
otherwise a plain function can be used.
1
The C++11 standard has added hi(lambda expression) em(lambda expressions) to
2
the language. As we've seen generic algorithms often accept an argument that
3
can either be a function object or it can be a plain function. Examples are
4
the tt(sort) and tt(find_if) generic algorithms. When the function called by
5
the generic algorithm must remember its state a function object is
6
appropriate, otherwise a plain function can be used.
8
8
The function or function object is usually not readily available. Often it
9
9
must be defined in or near the location where the generic algorithm is used.
10
The software engineer usually accomplished this by defining a class or
11
function in the anonymous namespace, passing an object of that class or
12
passing that function to a generic algorithm called from some other
13
function. If the latter function is itself a member function the need may be
14
felt to grant the function called by the generic algorithm access to other
15
members of its class. Often this results in a significant amount of code
16
(defining the class); or it results in complex code (to make available
17
software elements that aren't automatically accessible from the called
18
function (object)); and it may result -at the level of the source file- in
19
code that is irrelevant at the current level of specification. Nested classes
20
don't solve these problems and nested classes can't be used in templates.
22
A i(lambda function) is an
23
i(anonymous function)hi(function: anonymous). Such a function may be
24
defined on the spot and exists only during the lifetime of the statement
25
of which it is a part.
27
Here is an example of a lambda function:
10
Usually this is accomplished by defining a class or function in the anonymous
11
namespace, passing an object of that class or passing that function to a
12
generic algorithm called from some other function. If the latter function is
13
itself a member function the need may be felt to grant the function called by
14
the generic algorithm access to other members of its class. Often this results
15
in a significant amount of code (defining the class), or it results in complex
16
code (to make available software elements that aren't automatically accessible
17
from the called function (object)). It may also result in code that is
18
irrelevant at the current level of specification. Nested classes don't solve
19
these problems and nested classes can't be used in templates.
21
A i(lambda expression) defines an
22
i(anonymous function object)hi(function object: anonymous), also called a
23
emi(closure object). When a lambda expression is evaluated it results in a
24
temporary object (the closure object). The type of a closure object is called
25
its emi(closure type).
27
Lambda expressions may be used inside blocks, classes or namespaces (i.e.,
28
pretty much anywhere you like). Their implied closure type is defined in the
29
smallest block, class or namespace scope which contains the lamba
30
expression. The closure object's visibility starts at its point of definition
31
and ends where its closure type ends.
33
The closure type defines a (tt(const)) public inline function call
34
operator. Here is an example of a lambda expression:
38
[] // the `lambda-introducer'
39
(int x, int y) // the `lambda-declarator'
40
{ // a normal compound-statement
34
This particular function expects two tt(int) arguments and returns their
35
product. It could be used e.g., in combination with the tt(accumulate) generic
36
algorithm to compute the product of a series of tt(int) values stored in a
44
The function call operator of the closure object created by this lambda
45
expression expects two tt(int) arguments and returns their product. It is an
46
inline tt(const) member of the closure type. To drop the tt(const) attribute,
47
the lamba expression should specify tt(mutable), as follows:
49
[](int x, int y) mutable
52
The lambda-declarator may be omitted, if it does not contain
53
parameters. The parameters in a lamba declarator may not be provided with
56
A closure object as defined by the above lamda expression could be used e.g.,
57
in combination with the tt(accumulate) generic algorithm to compute the
58
product of a series of tt(int) values stored in a vector:
39
60
cout << accumulate(vi.begin(), vi.end(), 1,
40
61
[](int x, int y) { return x * y; });
42
63
The above lambda function uses the implicit return
43
64
hi(return type: implicit)
44
type tt(decltype(x * y)). An implicit return type can
45
only be used if the lambda function has a single statement of the form
46
tt(return expression;).
65
type tt(decltype(x * y)). An implicit return type can be used if the
66
lambda expression does not contain a tt(return) stattement (i.e., a void
67
lambda expression), if it contains a single tt(return) statement, or if it
68
contains multiple tt(return) statements returning values of identical types
69
(e.g., all tt(int) values).
48
Alternatively, the return type can be explicitly specified using a
71
If there are multiple tt(return) statements returning values of different
72
types then the lambda expression's return type must specified be explicitly
49
74
hi(return type: late-specified)i(late-specified return type),
50
75
(cf. section ref(AUTO)):
52
77
[](int x, int y) -> int
80
return x / static_cast<double>(y);
58
There is no need to specify a return type for lambda functions that do
59
not return values (i.e., a void lambda function).
61
Variables having the same scope as the lambda function can be accessed
62
from the lambda function using references, declared in between the lambda
63
function's square brackets. This allows passing the
64
i(local context) to lambda functions. Such variables are called a
65
emi(closure). Here is an example:
67
void showSum(vector<int> &vi)
86
Variables that are visible at the location of a lambda expression can be
87
accessed by the lambda expression. How these variables are accessed depends on
88
the contents of the lambda-introducer (the area between the square brackets,
89
called the the emi(lambda-capture)). The lambda-capture allows passing a
90
i(local context) to lambda expressions.
92
Visible global and static variables as well as local variables defined in
93
the lambda expression's compound statement itself can directly be accessed
94
and, if applicable, modified. Example:
100
[]() // [] may contain any specification
102
int localVariable = 0;
103
localVariable = ++global;
108
If the lambda expression is defined within a (non-static) class member
109
function then an initial tt(&) or tt(=) character in the lambda-capture
110
enables tt(this), allowing the lambda expression access to all class members
111
(data and functions). The class's data members can be modified.
113
If the lambda expression is defined inside a function then that function's
114
local variables that are visible at the point of the lambda expression's
115
definition can be accessed by the lambda expression.
117
An initial tt(&) character in the lambda-capture accesses these local
118
variables by reference. These variables can be modified from within the lambda
121
An initial tt(=) character in the lambda-capture creates a local copy of
122
the referred-to local variables. Furthermore, in this case the values of these
123
local copies can only be changed by the lambda expression if the lambda
124
expression is defined using the tt(mutable) keyword. E.g.,
133
++var; // modifies the local
134
} // copy, not fun's var
139
Fine-tuning is also possible. With an initial tt(=), comma-separated
140
tt(&var) specifications indicate that the mentioned local variables should be
141
processed by reference, rather than as copies; with an initial tt(&), comma
142
separated tt(var) specifications indicate that local copies should be used of
143
the mentioned local variables. Again, these copies have immutable values
144
unless the lambda expression is provided with the tt(mutable) keyword.
148
void showSum(vector<int> const &vi)
71
152
vi.begin(), vi.end(),
80
The variable tt(int total) is passed to the lambda function as a reference
81
(tt([&total])) and can directly be accessed by the function. Its parameter
82
list merely defines an tt(int x), which is initialized in sequence by each of
83
the values stored in tt(vi). Once the generic algorithm has completed
84
tt(showSum)'s variable tt(total) has received a value that is equal to the sum
85
of all the vector's values. It has outlived the lambda function and its value
88
If a closure variable is defined without the reference symbol (tt(&)) it
89
is interpreted as a value parameter of the lambda function that is initialized
90
by the local variable when the lambda function is passed to the generic
91
algorithm. Usually closure variables are passed by reference. If em(all) local
92
variables are to be passed by reference a mere ti([&]) can be used (to
93
pass the full closure by value ti([=]) should be used):
95
void show(vector<int> &vi)
100
vi.begin(), vi.end(),
107
std::cout << sum << ' ' << prod;
111
The specific internal implementation can vary, but the expectation is that the
112
lambda function will store the actual stack pointer of the function it is
113
created in, rather than individual references to stack variables.
116
It is also possible to pass some variables to lambda function by value and
117
other variables by reference. In that case the default is specified using
118
tt(&) or tt(=). This default specifying symbol is then followed by a list of
119
variables passed differently. Example:
126
Here tt(total) is passed by reference, tt(value) by value.
128
Class members may also define lambda functions. Such lambda functions have
129
full access to all the class's members. In other words, they are automatically
130
defined as friends of the class. Example:
158
std::cout << total << '\n';
161
The variable tt(int total) is passed to the lambda expression by reference
162
and is directly accessed by the function. Its parameter list merely defines an
163
tt(int x), which is initialized in sequence by each of the values stored in
164
tt(vi). Once the generic algorithm has completed tt(showSum)'s variable
165
tt(total) has received a value that is equal to the sum of all the vector's
166
values. It has outlived the lambda expression and its value is displayed.
168
Another fine-tuning consists of specifying tt(this) in the lambda-capture: it
169
also allows the lambda-expression to access the surrounding class members.
139
179
std::for_each(d_names.begin(), d_names.end(),
140
180
[this, &count](std::string const &name)
142
std::cout << ++count <<
143
this->capitalized(name) << '\n';
182
std::cout << ++count << ' ' <<
183
capitalized(name) << '\n';
148
std::string capitalized(std::string const &name);
188
std::string capitalized(std::string name);
151
Note the use of the tt(this) pointer: inside the lambda function it must
152
explicitly be used (so,nl()
153
tt(this->capitalized(name)) is used rather than
154
tt(capitalized(name))). In addition, in situations like these tt(this)
155
is automatically available when either tt([&]) or tt([=]) is used. So,
156
the above lambda function can also be be defined as:
158
[&](std::string const &name)
192
Lambda expressions may also be assigned to variables. An example of such
193
an assignment (using tt(auto) to define the variable's type) is:
200
The lifetime of such lambda expressions is equal to the lifetime of the
201
variable receiving the lambda expression as its value. Named lambda functions
202
nicely fit in the niche of local functions: when a function needs to perform
203
some computations that are at a conceptually lower level than the function's
204
task itself, then it's attractive to encapsulate these computations in a
205
separate support function, and call the support function where needed. A
206
support function can be defined in an anonymous namespace, but that quickly
207
becomes awkward when the requiring function is a class member, and the support
208
function needs access to the class's members as well. In that case a named
209
lambda expression can be used: it can be defined within the requiring
210
function, and may be given full access to the surrounding class. The name to
211
which the lambda expression is assigned becomes the name of a function which
212
can be called from the surrounding function. Here is an example, converting a
213
numeric IP address to a dotted decimal string, which can also be accessed
214
directly from an tt(Dotted) object (all implementations in-class to conserve
219
std::string d_dotted;
222
std::string const &dotted() const
226
std::string const &dotted(size_t ip)
229
[](size_t idx, size_t numeric)
160
std::cout << ++count <<
161
this->capitalized(name) << '\n';
231
return to_string(numeric >> idx * 8 & 0xff);
235
octet(3, ip) + '.' + octet(2, ip) + '.' +
236
octet(1, ip) + '.' + octet(0, ip);
165
Lambda functions may be assigned to variables. An example of such an
166
assignment (using tt(auto) to define the variable's type) is:
243
Now that lambda expressions have been covered let's see how they can be used
244
to avoid spurious returns from tt(condition_variable's wait) calls
245
(cf. section ref(CONDEX)). According to the C++11 standard, condition
246
variables may spuriously return from tt(wait) calls. Therefore it is necessary
247
to check that the data are actually available once tt(wait) continues.
249
The class tt(condition_variable) allows us to do that by offering tt(wait)
250
members expecting a lock em(and) a predicate. The predicate checks the data's
251
state, and returns tt(true) if the data's state allows the data's
252
processing. Here is an alternative implementation of the tt(down) member shown
253
in section ref(CONDEX), checking for the data's actual availability:
168
auto lambdaFun = [this]()
257
unique_lock<mutex> lock(sem_mutex);
261
return semaphore != 0
173
The lifetime of such lambda functions is equal to the lifetime of the
174
variable receiving the lambda function as its value.
267
The lambda expression ensures that tt(wait) only returns once
268
tt(semaphore) has been incremented.