This article is the fourth in the C++0x series, which is mainly about the new lambda expression, function object and bind mechanism in c++0x. The reason we put these three pieces together is because there is a very close relationship between the three blocks, and the understanding of this part of the content is deepened through comparative study. At the beginning, the first thing to say is a concept, closure (closure), which is the basis of understanding lambda. Let's look at Wikipedia's definition of closure in the field of computing:
A closure (also lexical closure, function closure or function value) is a function together witha referencing environment For the non-local variables of this function. |
The above-mentioned righteousness is that closure is a collection of functions and the context of the non-local variables it references. From the definition we can tell that closure can access variables beyond its definition, that is, the non-local vriables mentioned above, which greatly increases its skill. The most important application for closure is the callback function, which is the main reason why the function, bind, and Lambda are put together, and the three are recount in the process of using the callback function. The following is a step-by-step answer to the mystery of these three.
We know that in C + +, callable entities mainly include functions, function pointers, function references, which can be implicitly converted to the objects specified by the function, or objects that implement Opetator () (that is, functor in c++98). In c++0x, a new Std::function object is added, and the Std::function object is a type-safe package for existing callable entities in C + + (we know that such callable entities as function pointers are unsafe types). Let's take a look at some examples of function objects:
123456789101112131415161718 |
#include < functional> STD::function<size_t(ConstChar*)> Print_func;Normal function-Std::function objectsize_t Cprint(ConstChar*){ ...}print_func= Cprint;p Rint_func("Hello World"):Functor-Std::function Objectclass cxxprint{public : size_t operator) (const char*) {... }};p rint_func = p ;p rint_func" Hello World ") |
In the example above, we assign a normal function and a functor to a Std::function object, which we then invoke through the object. Other callable entities in C + + can be used as above. With the Std::function package, we can pass the callable entity like a normal object, which solves the problem of type safety. To understand the basic usage of std::function, let's take a look at some of the considerations used in the process:
- (1) The following two principles apply to the conversion of callable entities to std::function objects:
A. Parameters of the converted Std::function object can be converted to the parameters of the callable entity
B. The return value of a high-use entity can be converted to a Std::function object (note here that all callable entity return values are compatible with the return value of the Std::function object that returns void).
- (2) Std::function object can refer to satisfy any callable entity of the condition in (1)
- (3) The greatest use of std::function object is to implement a function callback, and the user needs to be aware that it cannot be used to check for equality or inequality
- 2. Bind
Bind is a mechanism that can pre-bind certain parameters of a specified callable entity to an existing variable, producing a new callable entity that is useful in the use of a callback function. In c++98, there are two functions bind1st and bind2nd, each of which can be used to bind the first and second parameters of functor, which are bound only to one parameter. Various restrictions make the availability of bind1st and bind2nd much less. c++0x, provides the std::bind, it binds the number of parameters is unrestricted, the specific parameters of the binding is not restricted, by the user specified, this bind is the real binding, with it, bind1st and bind2nd There is no useful, so C + + 0x is not recommended to use bind1st and bind2nd, are deprecated. Let's look at the usage of bind using the example below:
123456789101112131415161718 |
#include < functional>int Func(int x,int y);Auto Bf1= Std::Bind(Func,Ten, Std::Placeholders:: _1); bf1(20);< same as Func (10, 20)Class A{Public:int Func(int x,int y);}; A A;Auto BF2= Std::Bind(&a::Func, A, STD::Placeholders:: _1, STD::Placeholders:: _2); BF2(10,20);< same as A.func STD::function<intint) > bf3 = std::bind (&a::func, A, Std::placeholders ::_1, 100) ;bf3 (10; ///< Same as A.func (Ten) |
In the above example, BF1 is the first parameter of a two-parameter ordinary function is bound to 10, generating a new parameter of the callable body; BF2 is to bind a class member function to a class object and generate a new callable entity like a normal function; BF3 is to bind the class member function to the class object and the second parameter, resulting in a new Std::function object. To understand the above example, here are some things to note about using bind:
- (1) Bind pre-bound parameters need to pass specific variables or values in, for the pre-bound parameters, is Pass-by-value
- (2) For non-binding parameters, need to pass std::p laceholders go in, starting from the _1, in turn, increment. Placeholder is pass-by-reference.
- (3) The return value of BIND is a callable entity and can be assigned directly to the Std::function object
- (4) For binding pointers, arguments of reference types, the consumer needs to ensure that these parameters are available before callable entity calls
- (5) The This of a class can be bound by an object or a pointer
- 3. Lambda
Now that we've finished with function and bind, let's look at lambda. A friend with a python base, I'm sure it won't be a stranger to lambda. See the friends here, please recall the previous closure concept, Lambda is used to achieve closure. Its maximum use is also in the callback function, which is inextricably related to the function and bind mentioned earlier. Let's take a look at the lambda in the first instance:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 |
Vector<Int> VEC;1. Simple lambdaAuto it= Std::Find_if(VEC.Begin(), VEC.End(),[](int I){return I>50;});Class A{Public:BOOL operator(int I)Const{return I>50;}};Auto it= Std::Find_if(VEC.Begin(), VEC.End(), A());2. Lambda return SYNTAXSTD::function<Int(Int)> Square=[](int I)->Int{return I* I;}3. Lambda Expr:capture of local variable{int Min_val=10;int Max_val=1000;Auto it= Std::Find_if(VEC.Begin(), VEC.End(),[=](int I){return I> Min_val&& I< Max_val;});Auto it= Std::Find_if(VEC.Begin(), VEC.End(),[&](int I){return I> Min_val&& I< Max_val;});Auto it= Std::Find_if(VEC.Begin(), VEC.End(),[=,&max_value](int I){return I> Min_val&& I< Max_val;});}4. Lambda Expr:capture of class memberClass A{Public:void dosomething();Private: Std::vector<int> m_vecint m_min_val |
The above example basically covers the basic usage of lambda expression. We analyze each example one by one (the label is consistent with 1,2,3,4 in the code comment above):
- (1) This is the simplest lambda expression, it can be considered that the find_if of the lambda expression and the find_if using functor below are equivalent
- (2) This is a lambda expression with a return value, the syntax for the return value is shown above, and is written in the argument list after the parentheses. The return value can be omitted under the following conditions:
A. When the return value is void
B. The body of the lambda expression has return expr, and the type of expr is the same as the return value
- (3) This is an example of the lambda expression capture local variables, here are three small examples, the capture when the different syntax, the first small example = the capture of the variable pass-by-value, the second small to take out the & Pass-by-reference, a third small example of the capture's variable, specifies the default pass-by-value, but max_value this individual pass-by-reference
- (4) This is an example of a lambda expression capture class member variable, and here are three small examples. The first small example is the capture member variable through the this pointer, the second to third is by default way, but the second is through the Pass-by-value way, the third is through the pass-by-reference
After analyzing the above example, let's summarize some considerations when using lambda expressions:
- (1) Lambda expressions to use reference variables are subject to the following principles:
A. Local variables in the invocation context can only be referenced by capture (as shown in example 3 above)
B. Non-local local variables can be directly referenced
- (2) The user needs to be aware that the variables (primarily pointers and references) referenced by closure (the callable entity generated by the lambda expression) must be guaranteed to be available until the closure call is complete, consistent with the callable entities generated after the bind parameter above.
- (3) about the use of lambda is used to generate closure, and closure is also a callable entity, so you can std::function object to save the generated closure, you can also directly with auto
Through the above introduction, we basically understand the use of function, bind and lambda, combine the three, C + + will become very powerful, a bit of functional programming taste. Finally, here's a little bit more, for generating function with bind and using lambda expressions to generate function, normally both are OK, but when there are many parameters, bind will pass in many std::p laceholders, And it looks as if there is no lambda expression intuitive, so it is generally advisable to prioritize using lambda expressions.
C + + bind () function () and lambda application explained