Reprinted from: http://www.codeceo.com/article/cpp-lambda.html
C + + 11 support for LB , for those who like functional programming , is undoubtedly the best news. It enables C + + to enter the Hall of Fame represented by modern and popular programming languages such as C#,javascript.
Users who are unfamiliar with LB itself can see the MSDN article
(http://msdn.microsoft.com/en-us/library/dd293608.aspx), I just briefly analyze the use of LB in VC + +, implementation, and performance.
Nameless References
For a one-time, parametric expression, LB can save unnecessary class definition and maintenance, simplifying program design-maintenance costs.
For example, the following vector processing code, concise and clear:
Vector<int> v1 (10, 1);
int sum = 0;
For_each (V1.begin (), V1.end (), [&] (int i) {sum + = i;}) ;//line1
Otherwise, we have to define a function class that complicates such simple things. Using LB, we transfer the work of defining the function class to the compiler. VC + +, the implementation of the above LB compilation is to produce a stealth class:
Class _lambda_a01 {
int &capture1_;
Public
_LAMBDA_A01 (int &x): capture1_ (x) {}//line2
operator void (int i) {capture1_ + = i;}
};
At the time of reference (Line1), it becomes:
_LAMBDA_A01 lbd1 (sum);
for (auto A:v1) {
LDB1 (a);
}
Readers may wonder why C + + does not directly convert LB to inline expression, but instead generates a stealth class? This is because lb can actually be used as a "type" variable, which gives lb the same status as other classes. Like what:
Vector<int> v1 (10, 1);
int sum = 0;
For_each (V1.begin (), V1.end (), [&] (int i) {sum + = i;}) ;//line1
Vector<int> v2 (10, 1);
int sum2 = 0;
For_each (V1.begin (), V1.end (), [&] (int i) {sum2 + = i;}) ;//line2
If we use the above method,Line1 and Line2 repeating code, is a major taboo in software engineering. We can use the following LB usage Patterns:
Famous non-reference
Vector<int> v1 (10, 1);
Vector<int> v2 (10, 1);
int sum = 0;
Auto lb = [&] (int i) {sum + = i;} ;//line0
For_each (V1.begin (), V1.end (), LB);//line1
sum = 0; Line1.1
For_each (V1.begin (), V1.end (), LB});//line2
In Line0, we defined a well-known (lb)-free lb, which can be reused in Line1 and Line2 .
Note that the
1) Each LB "definition" will produce a new "stealth" class, so as far as possible with "famous reference", will reduce the size of the code, reducing the working set.
2) when defining, LB once "captures" the environment variable, so the above modified code adds Line1.1to correctly express the application logic.
3) Capture can be either "pass value" or "Pass Reference" (by reference). We Line0 use by reference.
Well-known reference
The above two lb usage modes are the main mode of lb application, which directly reflects the advantages of lb . On the other hand, since LB is nothing more than a stealth class, there is no reason why it cannot be used as a normal variable. This pattern is a simplified functor usage pattern. We can define LB as a std::function, such as the auto lb above can be defined as:
Std::function <void (int) > lb; LB is a function which takes an integer and returns void
Notice that this definition allows us to postpone assigning a value to a LB variable, or even a variable to multiple addresses (at different times). Here's a simple use case:
Struct mylambda
{
std::function <int (int) > _lbda;//line1
int _extra;< /p>
};
Mylambda testlambdaobj (int t)
{
Mylambda ret;
if (t = = 1)
{
Ret._extra = t;
Ret._lbda = [=] (int x) int {return t + x;};//line2
return ret;
}
Else
{
Ret._extra = t;
Ret._lbda = [=] (int x) INT {return T * x;};//LINE3
return ret;
}
}
void testlambdafun2 (int t)
{
MYLAMBDA ret = testlambdaobj (t);
int v = ret._lbda (t); //line4
printf ("V is '%d ' for type%d", V, t);
}
We first define the Mylambda data class and define a function member _LBDA, which, according to C + + SPEC, can be constructed from a lb transformation and is not very different from a normal class variable. Then we can assign it to the runtime (line2,line3) and use it as a normal function (line4).
Note that the following are:
- There is no concept of "closure" in the definition of function , and the formation of closures is implemented when LB is created (line2,line3).
- assigning lb to a function variable will inevitably result in the indirection (via function pointer) of the call (line4), which is equivalent to the virtual function, nor to the inline , of course, than to the direct invocation.
Closures (closure) are the unique added value of LB
If you ask why use lb instead of std::function? My answer is "closure."
C + + uses LB to implement closures, a syntax sugar that simplifies tedious class initialization. This is not an alternative to std::function . For example:
Auto sum = 0;
Auto Step = 2;
Auto lb = [&] (int i) {sum + = i + Step;} Capture sum and step by ref
lb forms its own closures, automatically capturing sum and stepfrom the environment, and if implemented in class , the above program adds at least 10 lines of code.
Research on LB performance
The following simple program tests four functions exactly the same, but uses the logic of different expressions:
1) t =1 with LB,
2) Direct expression when t=2
3) t=3 function
4) Use std::function to call LB indirectly when t=4
void testlambdafun(int t)
{
using namespace Std;
Vector<int> v1 (10, 1);
int x = 0;
int u = 0;
if (t = = 1)
{
clock_t begin = Clock ();
for (int i = 0; i < 100000; ++i)
{
For_each (V1.begin (),
V1.end (),
[&x, &u] (int i) {U + = i+ (x + +);}); //Line 1
}
clock_t end = Clock ();
Auto spent = double (end–begin)/clocks_per_sec;
printf ("Spent for type '%d ' are%f U is%d\n", T, spent, u);
}
else if (t = = 2)
{
clock_t begin = Clock ();
for (int i = 0; i < 100000; ++i)
{
Auto _first = V1.begin ();
Auto _last = V1.end ();
for (; _first! = _last; ++_first)
{
u = *_first+ (x + +); //Line 2
}
}
clock_t end = Clock ();
Auto spent = double (end–begin)/clocks_per_sec;
printf ("Spent for type '%d ' are%f U is%d\n", T, spent, u);
}
else if (t = = 3)
{
clock_t begin = Clock ();
for (int i = 0; i < 100000; ++i)
{
Auto _first = V1.begin ();
Auto _last = V1.end ();
for (; _first! = _last; ++_first)
{
Funcadd (U, x, *_first); //Line 3
}
}
clock_t end = Clock ();
Auto spent = double (end–begin)/clocks_per_sec;
printf ("Spent for type '%d ' are%f U is%d\n", T, spent, u);
}
else if (t = = 4)
{
clock_t begin = Clock ();
Std::function <void (int) > LBDA;
for (int i = 0; i < 100000; ++i)
{
LBDA = [&] (int i) {U + = i + (x + +);};
For_each (V1.begin (), V1.end (), lbda); //Line 4
}
clock_t end = Clock ();
Auto spent = double (end–begin)/clocks_per_sec;
printf ("Spent for type '%d ' are%f U is%d\n", T, spent, u);
}
}
void funcadd(int &u, int &x, int i)
{
U = i+ (x + +);
}
The following is the VC + + 2010 Test Results:
- In debug mode, the t=2 is the fastest, because the function calls are used for t=1,t=3,t=4, and the performance is certainly less than the inline expression.
- In release mode (select /ob1 optimization, inline extension for inline functions)
- T=1 and t=2 are exactly the same speed, averaging 3 times times faster than t=3. Of course, we can also Funcadd the same. The main purpose here is to prove that the performance ofLB is exactly the same as that of an expression after optimization. It proves that C + + lambda expression is not a syntax sugar of the stealth class, but a veritable expression.
T=4 is the slowest, and it is similar to t=3. However, due to the indirect invocation of Std::function's virtual function table, the/Ob1 optimization loses its function so that it not only calls a () operator, but is indirectly called through "virtual table". So from the performance, the LB through std::function indirect use, lost lb performance advantage.
Summarize
The C + + 11 lambda expression (lb), which can guarantee the same performance as inline expression, adds parameters and closures, and is a great tool for writing simple, clear and maintainable code. When applied, to avoid code duplication and increase the number of stealth classes, you can use a well-known non-type lb variable. LB can also be assigned to Std::function, which is used as a function pointer, but performance is less than simple to use inline.
The use of C + + Lambda expressions is detailed