In C + + 11, a lambda expression (usually called a "lambda") is an easy way to define an anonymous function object at the location where it is invoked or as a parameter passed to the function. A LAMBDA is typically used to encapsulate a small number of lines of code that are passed to an algorithm or asynchronous method. This article defines what a lambda is, compares the lambda with other programming techniques, describes its advantages, and provides a basic example.
parts of a LAMBDA expression
the ISO C + + standard shows a simple lambda passed to the Std::sort () function as a third parameter:
#include <algorithm>
#include <cmath>
void Abssort (float* x, unsigned n) {
std::sort (x, x + n,< c5/>//LAMBDA expression begins
[] (float A, float b) {return
(Std::abs (a) < Std::abs (b));
}/end of lamb Da expression
);
}
This figure shows the components of a lambda:
- The Capture clause (also called Lambda Boot in the C + + specification). )
- Parameter list (optional). (also known as the Lambda Declaration character)
- Variable specification (optional).
- Exception specification (optional).
- Trailing return type (optional).
- "Lambda Body"
Capture clause
A LAMBDA can introduce a new variable (with c++14) in its body, and it can also access (or "capture") the variables in the surrounding range. The lambda begins with a Capture clause (a lambda boot in standard syntax) that specifies the variable to be captured and whether it is captured by a value or a reference. A variable with a number (&) prefix is accessed by reference, and a variable without that prefix is accessed by value.
An empty capture clause [] indicates that the body of a lambda expression does not access variables in the enclosing scope.
You can use the default capture mode (Capture-default in standard syntax) to indicate how to capture any external variables referenced in a lambda: [ampersand] means that all variables referenced by a reference are captured, while [=] means capturing them by value. You can use the default capture mode, and then explicitly specify the opposite pattern for a specific variable. For example, if the lambda body accesses the external variable total by reference and accesses the external variable factor by value, the following capture clause is equivalent:
[&total, Factor]
[Factor, &total]
[Ampersand, Factor]
[Factor,]
[=, &total]
[&total, =]
When using Capture-default, only the variables mentioned in the lambda are captured.
If the capture clause contains capture-default&, there is no capture in the identifier of the capture clause that can take the form of & identifier. Similarly, if the capture clause contains capture-default=, the capture of the capture clause cannot take the form of = identifier. Identifier or this cannot occur more than once in the capture clause. Some examples are given in the following code fragment.
struct S {void f (int i);};
void s::f (int i) {
[ampersand, i]{};//OK
[ampersand &i]{};//error:i preceded by & where & is the default< c4/>[=, this]{}; Error:this when =
is the default [I, i]{};//error:i repeated
}
The capture followed by an ellipsis is a package extension, as shown in the following variable parameter template example:
Template<class ... Args>
void f (Args ... Args) {
auto x = [Args ...] {return g (args ...);};
X ();
}
To use a lambda expression in the body of a class method, pass the this pointer to the CAPTURE clause to provide access to the methods and data members of the enclosing class. For an example showing how to use a lambda expression with a class method, see "Example: Using a lambda expression in a method" in an example of a lambda expression.
When using the capture clause, it is recommended that you remember the following points (especially when using a multithreaded lambda):
A reference capture can be used to modify an external variable, but a value capture does not implement this action. (mutable allows you to modify the copy, not the original item.) )
A reference capture reflects an update of an external variable, while a value capture is not reflected.
A reference capture introduces a lifetime dependency, while a value capture does not have a lifetime dependency. This is especially important when the lambda is running asynchronously. If a local variable is captured by reference in an asynchronous lambda, the local variable will most likely disappear when the lambda runs, resulting in a run-time access violation.
Universal Capture (C++14)
in c++14, new variables can be introduced and initialized in the Capture clause without having to make these variables exist within the enclosing scope of the lambda function. Initialization can be represented by any arbitrary expression, and the type of the new variable will be inferred from the type generated by the expression. One advantage of this feature is that in c++14, only moving variables (such as STD::UNIQUE_PTR) can be captured from the perimeter and used in the lambda.
Pnums = make_unique<vector<int>> (nums);
//...
Auto A = [ptr = Move (pnums)] ()
{
//use PTR
};
Parameter list
In addition to capturing variables, a lambda can accept input parameters. The argument list (called the Lambda Declaration in standard syntax) is optional and is similar in most respects to a function's argument list.
int y = [] (int-A, int second) {return-A-
second;
};
In c++14, if the parameter type is generic, you can use the Auto keyword as the type descriptor. This tells the compiler to create the function call operator as a template. Each auto instance in the argument list is equivalent to a different type parameter.
Auto y = [] (auto second) {return a
/
second;
};
A lambda expression can use another lambda expression as its argument. For more information, see "Higher-order Lambda expressions" in the example topic of LAMBDA expressions.
Because the argument list is optional, the argument is not passed to a lambda expression and its lambda-declarator: does not contain exception-specification, trailing-return-type, or mutable , you can omit the empty parentheses.
Variable specification
Typically, the function invocation operator of a lambda is const-by-value, but the use of the mutable keyword can be canceled. It does not generate a mutable data member. With the variable specification, the body of a lambda expression can modify a variable that is captured by a value. Some examples later in this article will show you how to use mutable.
Exception Specification
You can use the throw () exception specification to indicate that a lambda expression does not throw any exceptions. As with normal functions, if a lambda expression declares an C4297 exception specification and the lambda body throws an exception, the Visual C + + compiler generates a warning throw (), as follows:
Throw_lambda_expression.cpp
//compile with:/W4/EHSC
int main ()//C4297 expected
{
[] () throw () {th Row 5; }();
}
return type
The return type of the lambda expression is automatically inferred. You do not need to use the Auto keyword unless you specify a trailing return type. Trailing-return-type is similar to the return type part of a normal method or function. However, the return type must be followed by the argument list, and you must include the Trailing-return-type keyword-> before the return type.
If the lambda body contains only one return statement or its expression does not return a value, you can omit the part of the return type of the lambda expression. If the lambda body contains a single return statement, the compiler infers the return type from the type of the return expression. Otherwise, the compiler infers the return type to void. The following code example fragment illustrates this principle.
Auto X1 = [] (int i) {return i;}; Ok:return type is int
auto x2 = []{return{1, 2};}//Error:return type are void, deducing
//return type From Braced-init-list are not valid
A lambda expression can generate another lambda expression as its return value. For more information, see "Higher-order lambda expressions" in examples of lambda expressions.
LAMBDA Body
the lambda body of a lambda expression (compound-statement in standard syntax) can contain any content that the body of a normal method or function can contain. Both normal functions and the body of a lambda expression can access the following variable types:
- Capture variables from the enclosing range, as described earlier.
- Parameters
- Declaring variables locally
- Class data member (when declaring and capturing this within a class)
- Any variable that has a static storage duration (for example, global variables)
The following example contains a lambda expression that explicitly captures the variable n by value and implicitly captures the variable m by reference:
Captures_lambda_expression.cpp
//compile with:/w4/ehsc
#include <iostream>
using namespace std ;
int main ()
{
int m = 0;
int n = 0;
[Ampersand, N] (int a) mutable {m = ++n + A;} (4);
cout << m << endl << n << endl;
}
Output:
Because variable n is captured by value, the value of the variable remains unchanged by 0 after the lambda expression is invoked. The mutable specification allows you to modify n in a lambda.
Although a lambda expression can only capture variables that have an automatic storage duration, you can use a variable with a static storage duration in the body of a lambda expression. The following example uses the Generate function and the lambda expression to assign values to each element in a vector object. A lambda expression modifies a static variable to produce the value of the next element.
void Fillvector (vector<int>& v)
{
//A local static variable.
static int nextValue = 1;
The lambda expression that appears in the following call to
//The Generate function modifies and uses the local St atic
//variable nextValue.
Generate (V.begin (), V.end (), [] {return nextvalue++;});
Warning:this is isn't Thread-safe and is shown to illustration only
}
The following code example uses the functions in the previous example and adds an example of a lambda expression that uses the STL algorithm generate_n. The lambda expression assigns the elements of a vector object to the sum of the first two elements. The mutable keyword is used so that the body of a lambda expression can modify the copy of the external variable x and y that the lambda expression captures by value. Because lambda expressions capture the original variables x and y by value, their values are still 1 after the lambda is executed.
Compile with:/w4/ehsc #include <algorithm> #include <iostream> #include <vector> #include <stri
Ng> using namespace std;
Template <typename c> void print (const string& s, const c& C) {cout << s;
for (const auto& e:c) {cout << e << "";
} cout << Endl;
} void Fillvector (vector<int>& v) {//A local static variable.
static int nextValue = 1; The lambda expression that appears in the following call to//the Generate function modifies and uses the local Stati
c//Variable NextValue.
Generate (V.begin (), V.end (), [] {return nextvalue++;}); Warning:this is isn't Thread-safe and is shown to illustration only} int main () {//The number of elements in the VE
ctor.
const int elementcount = 9;
Create a vector object with each element set to 1.
Vector<int> V (elementcount, 1);
These variables hold the previous two elements of the vector.
int x = 1;
int y = 1; SeTS per element in the vector to the sum of the//previous two elements. Generate_n (V.begin () + 2, elementCount-2, [=] () mutable throw ()-> int {//lambda is the 3rd parameter//Generat
E Current value.
int n = x + y;
Update previous two values.
x = y;
y = n;
return n;
});
Print ("Vector v" after called to Generate_n () with lambda: ", v);
Print The local variables x and Y.
The values of x and Y hold their initial values because//They are captured by value.
cout << "x:" << x << "y:" << y << Endl;
Fill the vector with a sequence of numbers fillvector (v);
Print ("Vector v after 1st called to Fillvector ():", V);
Fill the vector with the next sequence of numbers Fillvector (v);
Print ("Vector v after 2nd called to Fillvector ():", V);}
Output:
Vector v after call to Generate_n () and Lambda:1 1 2 3 5 8
x:1 y:1
Vector v after 1st call to Fillvec Tor (): 1 2 3 4 5 6 7 8 9
vector v after 2nd call to Fillvector (): 10 11 12 13 14 15 16 17-18