Introduction of Lambda expression
Some function object classes are only used to define an object, and this object is used only once, which is a bit wasteful. Moreover, defining a function object class and using it can be far apart, and it can be cumbersome to see exactly what its operator() member function does.
For function object classes that are only used once, can they be defined directly where they are used?
Lambda expressions solve this problem by defining and creating anonymous function objects and reducing the number of function object classes in the program to simplify programming.
The syntax format of Lambda is as follows:
[Capture List] Captures contextual variables for use by Lambda functions.
[Parameter List] If parameter passing is not required, it can be omitted with ().
[mutable] Variable specification. By default, the Lambda function is always a const function, and mutable can cancel its constancy. When using this modifier, the parameter list cannot be omitted (even if there are no parameters).
[throw] exception declaration. An exception thrown by a specified function. This part and mutable are added according to the actual situation and can be omitted.
[Return value type], when the return value type is void or the return value type is clear, you can omit this section and let the compiler automatically infer the return value type.
Function body cannot be omitted, but it can be empty
Instance demo
Capturing lists using lambda expressions
- [] No variables are captured.
int main() { int a = 0, b = 1; auto f1 = [] { return a; }; // error, no external variables captured }
- [&] Captures all variables in the external scope and uses them as references in the body of the function (captured by reference).
int main() { int a = 0, b = 1; auto f2 = [&] { return a++; }; // OK, captures all external variables, and performs a self-addition operation on a cout << f2() << endl;//0 cout << a << endl;//1 }
- [=] Captures all variables in the external scope and uses them as copies in the function body (captured by value).
int main() { int a = 0, b = 1; auto f3 = [=] { return a; }; // OK, captures all external variables, and returns a auto f4 = [=] { return a++; }; // error, a is captured by copying and cannot be modified cout<<f3()<<endl;//0 cout<<a<<endl;//0 }
- [=, &foo] Captures all variables in the external scope by value and foo variables by reference.
int main() { int a = 0, b = 1; auto f7 = [=, &b] { return a + (b++); }; // OK, captures all references to external variables and b, and adds B by itself cout << f7() << endl;//1 cout << a << endl;//0 cout << b << endl;//2 }
- [bar] Captures the bar variable by value without capturing other variables.
int main() { int a = 0, b = 1; auto f5 = [a] { return a + b; }; // error, no capturing variable b }
- [this] captures this pointer in the current class , Make the lambda expression have the same access rights as the current class member function. If you've already used &or=, add this option by default. This is captured so that member functions and member variables of the current class can be used in lamda.
class A { public: int i_ = 0; int getvalue() { return i_; } void func(int x, int y) { auto x1 = [] { return i_; }; // error, no external variables captured auto x2 = [=] { return i_ + x + y; }; // OK, capture all external variables auto x3 = [&] { return i_ + x + y; }; // OK, capture all external variables auto x4 = [this] { return i_; }; // OK, capture this pointer auto x5 = [this] { return i_ + x + y; }; // error, no x,y captured auto x6 = [this, x, y] { return i_ + x + y; }; // OK, capture this pointer, x, y auto x7 = [this] { return i_++; }; // OK, captures this pointer, and modifies the member's value auto x8 = [this] { return getvalue(); }; // OK, captures this pointer, accesses member functions of the current class } };
Lambda parameter list
int main() { int a = 0, b = 1; //Reference auto function1 = [](int first, int second) { return first + second; }; cout << function1(a,b)<< endl; //No reference auto function2 = [] { cout<<"hello world"<<endl; }; function2(); }
Variable specification mutable
int main() { int i = 100; //Auto function 1 = [i] {I = 10;} // error, captured I is the const attribute auto function2 = [i]()mutable { i = 10; return i; }; // The mutable modifies the const property of the captured i, but I itself is not modified cout << function2() << endl;//10 cout << i << endl;//100 }
3. The Essence of Lambda Expression
A lambda expression essentially generates an anonymous function class object and overrides the operator() method in the anonymous function class.
int main() { auto print = [] {cout << "hello world" << endl; }; print(); }
The compiler will translate the above sentence into the following code:
class printclass { public: void operator()()const { cout << "hello world" << endl; } }; int main() { //Create an object with the class's constructor, where print is a function object auto print = printclass(); print(); }
4. Affine Functions (Function Objects)
A class of overloaded function call operators (), whose object is called a function object, which calls an overloaded operator() behaves like a function, also known as a function-like function.
class printclass { public: void operator()()const { cout << "hello world" << endl; } }; int main() { //print is now a function object auto print = printclass(); print();//Function object call overload () is similar to a function, so it is also called a function-like }
Two highlights of function objects are:
1) Function objects can contain states;
2) The function object is a type and can be used as a template parameter.
5. Scenarios for Lamdba Expression
Lamdba expression applied to STL algorithm library
#include<iostream> #include<algorithm> using namespace std; int main() { auto Mygreater = [](int a, int b) {return a < b; }; int a[] = { 45,12,34,77,90,11,2,4,5,55 }; sort(a, a + 10, Mygreater); for (int i = 0; i < 10; i++) { cout << a[i] << " "; } }
(2) Lamdba expression is applied to function pointer and function
#include<iostream> #include<algorithm> #include <functional> using namespace std; int main() { int x = 8, y = 9; auto add = [](int a, int b) { return a + b; }; std::function<int(int ,int)> Add = [](int a, int b) { return a + b; }; cout << "add: " << add(x, y) << endl; cout << "Add: " << Add(x, y) << endl; }
(3) Lamdba expression as a function parameter
#include<iostream> #include <functional> using namespace std; using FuncCallback = std::function<void(void)>; void DataCallback(FuncCallback callback) { std::cout << "Start FuncCallback!" << std::endl; callback(); std::cout << "End FuncCallback!" << std::endl; } auto callback_handler = [&]() { std::cout << "This is callback_handler"<<endl; }; int main() { DataCallback(callback_handler); }