C ++ understands function objects and lambda expressions
Refer to Chapters 21st and 22nd of 21-day learning C ++ to introduce function objects. At the same time, you can use lambda expressions as an anonymous function object to gain a more concise understanding of function objects. The main content of this blog is:
(1) concepts of function objects;
(2) Use Function objects as predicates;
(3) how to use a function object to implement a one-and binary predicates;
(4) how to compile lambda expressions;
(5) how to use a lambda expression as a predicate;
(6) how to compile a stored and operable lambda expression.
I. Function objects 1. Concepts and types of function objects
Function objects are C ++ objects. In terms of concept, function objects are used as function objects; in terms of implementation, function objects are class objects that implement operator. Although functions and function pointers can also be regarded as function objects, only objects that absorb operator () classes can be saved and used in standard template library algorithms. C ++ function objects commonly used in STL algorithms can be divided into the following two types: (1) a mona1 function: a function that accepts a parameter. If a mona1 function returns a Boolean value, it is called a predicate; (2) binary function: a function that accepts two parameters. If bool is returned, it is called a binary predicate function. Returns an algorithm that is often used to judge bool worthy function objects. The function objects that combine two function objects are called adaptive function objects.
2. Typical usage of function objects (1) usage of mona1 Functions
For the STL algorithm std: for_each (), three parameters are accepted. The first parameter specifies the start point of the range and the second parameter specifies the end point of the range, the third parameter is the function to be called for each element within the specified range. This function can be implemented using a single-element predicate, for example:
Template
Struct DisplayElement
{
Void operator () (const elementType & element) const
{
Cout <element <'';
}
};
Vector VectorElement;
For_each (vectorElement. begin (), vectorElement. end (), DisplayElement ());
In this way, the STL algorithm for_each can be used to execute the same function method for data within the specified range. Here you can also use an anonymous function object, that is, a lambda expression, as shown below, to transform the preceding for_each into the same function:
For_each (vectorElement. begin (), vectorElement. end (), [] (int & element) {cout <element <'';});
If you can use structured objects to store information, the advantages of function objects implemented in the structure will be displayed.
(2) usage of The unary predicates
The unary function that returns a Boolean value is a predicate. This function can be used to judge the STL algorithm.
One-dimensional predicates are widely used in STL algorithms. For example, std: partition uses one-dimensional predicates to divide the range. The stable_partition algorithm also uses one-dimensional predicates to divide the range, but the relative sequence of elements remains unchanged. Functions such as std: find_if and std: remove_if Delete elements also use a one-dimensional predicate. std: remove_if deletes the elements that meet the predicate conditions within the specified range.
(3) usage of binary Functions
If the function f (x, y) returns a value based on the input parameter, it will be useful. This binary function can be used to perform operations on two operands, such as addition, subtraction, multiplication, division, and so on. Also implement the most important operator (). In std: transform and other algorithms, the binary product function can be used to calculate the dot multiplication of the content of two containers.
Template
Class Multiply
{
Public:
ElementType operator () (const elmentType & elem1, const elmentType & elem2)
{Return (elem1 * elem2 );}
}
Vector VecMultiplicand, vecMultiplier;
Vector VecResult;
Transform (vecMultiplicand. begin (), vecMultiplicand. end (), vecMultiplier. begin (), vecResult. begin (), Multiply ());
Transform multiply the content of the two ranges and store the results in the third range. The three scopes are stored in vecMultiplicand, vecMultiplier, and vecResult of the std: vector type. The multiplication operation is performed by calling the binary function Multiply: operator (). This function is called for each element in the source range and target range. The return value of operator () is saved in vecResult.
(4) usage of binary predicates
A binary predicate is a function that accepts two parameters and returns a Boolean value. This function is used in STL functions such as std: sort. Binary predicates are used for sorting, and operator () is used for dynamic sorting.
Many STL algorithms use binary predicates, such as deleting std: unique () of adjacent duplicate elements, sorting algorithm sort, and sorting and maintaining the relative order of std :: stable_sort and std: transform that operates in two ranges.
(5) Summary
When a function object is implemented in a structure or class, it is more useful than a simple function because it can also be used to store state-related information. A predicate is a special type of function object.
II. C ++ lambda expression 1. lambda expression Concept
Lambda expressions can be considered as anonymous structures (or classes) that contain public operator (). In this sense, lambda expressions belong to function objects. From the analysis described above:
For_each (vectorElement. begin (), vectorElement. end (), [] (int & element) {cout <element <'';});
When the compiler is added to the following lambda expressions: [] (int & element) {cout <element <'';} automatically expands it into a DisplayElement with a similar structure. :
Struct DisplayElement
{
Void operator () (const int & element) const
{
Cout <element <'';
}
};
2. How to define lambda expressions
Start with square brackets [] and tell the compiler that a lambda expression is followed by a parameter list. This parameter list is provided to operator () When lambda expressions are not used () the parameter list is the same.
3. lambda expressions corresponding to mona1 Functions
The lambda expression corresponding to the operator (Type) receives a parameter, which is defined as follows:
[] (Type paramName) {// lambda expression code here} can also be referenced to transmit the parameter: [] (Type & paramName) {// lambda expression code here }.
4. The lambda expression corresponding to the unary predicates
A predicate can be used to make a decision. A single-element predicate is a one-dimensional expression of the bool type. Example:
[] (Int & Num) {return (Num % 2) = 0 );}
Here, the attribute of the return value is to let the compiler know that the return type of the lambda expression is bool. In the algorithm, lambda expressions can be used as one-dimensional predicates. For example, you can use the aforementioned lambda expressions in find_if to find the even numbers in the set. If the predicate returns true, find_if returns an iterator pointing to the corresponding element, indicating that an element meeting the condition is found.
5. Accept the lambda expression of the status variable by capturing the list
True is returned when the preceding unary predicate can be divisible by 2. If it is more common, true is returned when the number can be divisible by the specified divisor. The status variable can be used:
Int Divisor = 2;
[Divisor] (int & Num) {return (Num % Divisor) = 0 );}
A series of parameters passed in the form of state variables, also known as the capture list of lambda expressions.
6. General Syntax of lambda expressions
[StateVar1, StateVar1] (Type & param ){//}
To modify these state variables in a lambda expression, you can add the keyword multable:
[StateVar1, StateVar1] (Type & param) multable {//}
In this way, you can modify the variables specified in the capture list [] in the lambda expression. However, after leaving the lambda expression, these modifications will be invalid, to ensure that changes to state variables within the lambda expression are also valid outside the lambda expression, pass them as reference:
[& StateVar1, & StateVar1] (Type & param ){//}
Lambda expressions can also accept multiple input parameters, which can be separated by commas:
[StateVar1, StateVar1] (Type1 & param1, Type2 & param2 ){//}
To explicitly specify the return type to the compiler, use->, as shown below:
[StateVar1, StateVar1] (Type1 & param1, Type2 & param2)-> ReturnType {return (value or expression );}
Finally, the compound statement {} can contain multiple statements separated by semicolons:
[StateVar1, StateVar1] (Type1 & param1, Type2 & param2)-> ReturnType {Statement 1; Statement 2; return (value or expression );}
If a lambda expression contains multiple lines of code, the return type must be explicitly specified.
7. lambda expressions corresponding to binary Functions
The binary function accepts two parameters and returns a value. The equivalent lambda expression is as follows:
[...] (Type1 & param1, Type2 & param2 ){//}
8. lambda expressions corresponding to binary predicates
Returns true or false. Binary functions that help decision-making are called binary predicates. This type of predicate can be used in sorting algorithms such as std: sort.
[...] (Type1 & param1, Type2 & param2) {return bool expression ;}
9. Summary
Lambda expressions can be used only when they are concise and efficient;
Remember that lambda expressions always start;
The status variables specified in the capture list cannot be modified unless the keyword multable is specified;
Lambda expressions are anonymous classes or structures that implement operator;
Use const to limit parameters;
When a lambda expression statement block contains multiple statements, you must explicitly specify the return type;
Instead of using long lambda expressions that contain multiple statements, you should use function objects instead, because every time you use a lambda expression, you must redefine it, which does not help improve code reusability.