Overview
A set of statements that perform tasks can be regarded as a function and a callable object. In the process of programming, we are accustomed to abstracting a group of statements with reusability into functions and abstracting the changing parts into functions parameters.
The use of functions can greatly reduce the repetition rate of code and improve the flexibility of code.
There are many ways to have functions in C++. As far as function calls are concerned
func(param1, param2);
Here we use func as the function call name, and param1 and param2 as function parameters. In C++, the type of func may be:
- Normal functions
- Class member functions
- Class static functions
- Simulation functions
- Function pointer
- Lambda expression C++11 adds standard
- std::function C++11 adds standard
The following is an introduction to these functions
Simple function form
Normal functions
This kind of function definition is relatively simple and is generally declared at the beginning of a file. as follows:
#include <iostream> // Ordinary function declarations and definitionsint func_add(int a, int b) { return a + b; } int main() { int a = 10; int b = 20; int sum = func_add(a, b); std::cout << a << "+" << b << "is : " << sum << std::endl; getchar(); return 0; }
Class member functions
Functions defined in a class class are generally called class methods, which are divided into member methods and static methods. The difference is that the parameter list of member methods implies the class this pointer.
#include <iostream> class Calcu { public: int base = 20; // Member method of the class, the parameters include this pointer int class_func_add(const int a, const int b) const { return this->base + a + b; }; // Static member method of the class does not contain this pointer static int class_static_func_add(const int a, const int b) { return a + b; }; }; int main(void) { Calcu obj; int a = 10; int b = 20; // The normal class member method call is as follows obj.class_func_add(a, b); // The class static member method is called as follows obj.class_static_func_add(a, b); Calcu::class_static_func_add(a, b); getchar(); return 0; }
Simulation functions
Fiction functions use classes to simulate function call behavior. We just need to overload the operator() method of a class and call the class like a function. This method is used less frequently.
class ImitateAdd { public: int operator()(const int a, const int b) const { return a + b; }; }; int main() { // First create a functor object, and then call the() operator to simulate the function call ImitateAdd imitate; imitate(5, 10); getchar(); return 0; }
Function pointer
As the name implies, a function pointer can be understood as a pointer to a function. You can assign the function name to a function pointer of the same type, and call the function by calling the function pointer.
Function pointers are a standard C/C++ callback function usage solution, which itself provides great flexibility.
#include <iostream> // Declare a compute function pointer, the function parameters are two int types, and the return value is int typeint (*compute)(int, int); int max(int x, int y) { return x >= y ? x : y; } int min(int x, int y) { return x <= y ? x : y; } int add(int x, int y) { return x + y; } int multiply(int x, int y) { return x * y; } // A function containing function pointers as callbacksint compute_x_y(int x, int y, int(*compute)(int, int)) { // Call callback function return compute(x, y); } int main(void) { int x = 2; int y = 5; std::cout << "max: " << compute_x_y(x, y, max) << std::endl; // max: 5 std::cout << "min: " << compute_x_y(x, y, min) << std::endl; // min: 2 std::cout << "add: " << compute_x_y(x, y, add) << std::endl; // add: 7 std::cout << "multiply: " << compute_x_y(x, y, multiply) << std::endl; // multiply: 10 // Lambda without capture can be converted to function pointers of the same type auto sum = [](int x, int y)->int{ return x + y; }; std::cout << "sum: " << compute_x_y(x, y, sum) << std::endl; // sum: 7 getchar(); return 0; }
Lambda Functions
Lambda function definition
Lambda functions, also known as Lambda expressions or anonymous functions, are added to C++11. The definition form is as follows:
[captures] (params) -> return_type { statments;}
in:
- [captures] is a capture list, used to capture outer variables
- (params) is an anonymous function parameter list
- -> return_type Specifies the return value type of anonymous function
- { statistics; } part is a function body, including a series of statements
Need to note:
- When an anonymous function has no parameters, the (params) part can be omitted
- When the return value of the anonymous function body has only one type or the return value is void, the -> return_type part can be omitted
- When defining anonymous functions, auto is generally used as anonymous function type.
The following are valid anonymous function definitions
auto func1 = [](int x, int y) -> int { return x + y; }; auto func2 = [](int x, int y) { return x > y; }; // Omit the return value typeauto func3 = [] { global_ip = 0; }; // Omit the parameter part
Lambda function capture list
In order to be able to use variables in external scopes in Lambda functions, it is necessary to specify which variables to use in [].
Here are the various capture options:
- [] No variables are captured
- [&] Capture all variables in the external scope and use them as references in anonymous function bodies
- [=] Capture all variables in the external scope and copy a copy to use in anonymous function body
- [x, &y] x capture by value, y capture by reference
- [&, x] x captured by value. Other variables captured by reference
- [=, &y] y is captured by reference. Other variables are captured by value
- [this] Capture this pointer in the current class. If & or = is already used, this option will be added by default.
Only when the lambda function does not specify any capture can it be explicitly converted to a function pointer with the same declaration form.
auto lambda_func_sum = [](int x, int y) { return x + y; }; // Define lambda functionvoid (*func_ptr)(int, int) = lambda_func_sum; // Assign the lambda function to the function pointerfunc_ptr(10, 20); // Calling function pointer
std::function function wrapper
std::function definition
std::function has been added to the standard after C++11. It can be used to describe all callable entities in C++. It is a wrapper for callable objects, and the declaration is as follows:
#include <functional> // Declare a callable object type with a return value of int and a parameter of two intsstd::function<int(int, int)> Func;
Before using it, you need to import the <functional> library and use it through the std namespace.
Other function entities are converted to std::function
What makes std::function powerful is that it is compatible with all function entities with the same parameter type.
Compared with function pointers, std::function is compatible with lambda functions with capture and provides support for class member functions.
#include <iostream> #include <functional> std::function<int(int, int)> SumFunction; // Normal functionsint func_sum(int a, int b) { return a + b; } class Calcu { public: int base = 20; // Member method of the class, the parameters include this pointer int class_func_sum(const int a, const int b) const { return this->base + a + b; }; // Static member method of the class does not contain this pointer static int class_static_func_sum(const int a, const int b) { return a + b; }; }; // Simulated functionsclass ImitateAdd { public: int operator()(const int a, const int b) const { return a + b; }; }; // lambda functionauto lambda_func_sum = [](int a, int b) -> int { return a + b; }; // Function pointerint (*func_pointer)(int, int); int main(void) { int x = 2; int y = 5; // Normal functions SumFunction = func_sum; int sum = SumFunction(x, y); std::cout << "func_sum:" << sum << std::endl; // Class member functions Calcu obj; SumFunction = std::bind(&Calcu::class_func_sum, obj, std::placeholders::_1, std::placeholders::_2); // Bind this object sum = SumFunction(x, y); std::cout << "Calcu::class_func_sum:" << sum << std::endl; // Class static functions SumFunction = Calcu::class_static_func_sum; sum = SumFunction(x, y); std::cout << "Calcu::class_static_func_sum:" << sum << std::endl; // lambda function SumFunction = lambda_func_sum; sum = SumFunction(x, y); std::cout << "lambda_func_sum:" << sum << std::endl; // Lambda function with capture int base = 10; auto lambda_func_with_capture_sum = [&base](int x, int y)->int { return x + y + base; }; SumFunction = lambda_func_with_capture_sum; sum = SumFunction(x, y); std::cout << "lambda_func_with_capture_sum:" << sum << std::endl; // Simulated functions ImitateAdd imitate; SumFunction = imitate; sum = SumFunction(x, y); std::cout << "imitate func:" << sum << std::endl; // Function pointer func_pointer = func_sum; SumFunction = func_pointer; sum = SumFunction(x, y); std::cout << "function pointer:" << sum << std::endl; getchar(); return 0; }
The final output is as follows:
func_sum:7
Calcu::class_func_sum:27
Calcu::class_static_func_sum:7
lambda_func_sum:7
lambda_func_with_capture_sum:17
imitate func:7
function pointer:7
It is necessary to note that for class member functions, because class member functions contain this pointer parameters, it is not enough to use std::function alone. You also need to use std::bind function to bind this pointer and parameter list.
std::bind parameter binding rules
When using std::bind to bind class member functions, you need to pay attention to the order of binding parameters:
// Follow the example aboveSumFunction = std::bind(&Calcu::class_func_sum, obj, std::placeholders::_1, std::placeholders::_2); SumFunction(x, y);
- The first parameter is a reference to the class member function name (referred to use)
- The second parameter is this pointer context, that is, a specific object instance
- The following parameters are respectively formulated in order of 1, 2, and 3 of the class member function.
- Use std::placeholders::_1 to represent the first parameter of the calling procedure as the member function parameter
- std::placeholders::_n represents the nth parameter when calling
See the following example:
// The first parameter of the binding member function is 4 and the second parameter is 6SumFunction = std::bind(&Calcu::class_func_sum, obj, 4, 6); SumFunction(); // Value is 10 // The first parameter of the binding member function is the first parameter when called, and the second parameter is 10SumFunction = std::bind(&Calcu::class_func_sum, obj, std::placeholders::_1, 10); SumFunction(5); // Value is 15 // The first parameter of the binding member function is the second parameter when called, and the first parameter is the second parameter when calledSumFunction = std::bind(&Calcu::class_func_sum, obj, std::placeholders::_2, std::placeholders::_1); SumFunction(5, 10); // The value is 15
For non-class member objects, you can usually just assign values directly, and the parameters will be automatically converted and bound. Of course, you can also use std::bind to specify the parameter binding behavior;
#include <iostream> #include <functional> // Output x, y, x in ordervoid print_func(int x, int y, int z) { std::cout << x << " " << y << " " << z << std::endl; } std::function<void(int, int, int)> Func; int main() { Func = std::bind(&print_func, 1, 2, 3); Func(4, 5, 6); // Output: 1 2 3 Func = std::bind(&print_func, std::placeholders::_2, std::placeholders::_1, 3); Func(1, 2, 7); // Output: 2 1 3 int n = 10; Func = std::bind(&print_func, std::placeholders::_1, std::placeholders::_1, n); Func(5, 6, 7); // Output: 5 5 10 getchar(); return 0; }
Need to note:Even if the default parameters are specified during binding, the same number of parameters must be specified when calling (although it will not work), otherwise the compilation will not be passed.
About callback functions
A callback is to specify the callee behavior at the caller level by taking functions, etc. as parameters of another function.
Through the above introduction, we know that you can use function pointers and std::function as function parameter type to implement callback functions:
#include <iostream> #include <functional> std::function<int(int, int)> ComputeFunction; int (*compute_pointer)(int, int); int compute1(int x, int y, ComputeFunction func) { // do something return func(x, y); } int compute2(int x, int y, compute_pointer func) { // do something return func(x, y); } // Refer to the above function pointer andstd::functionExamples
The above two methods are equivalent to general functions and simple lambda functions.
However, if there are special parameter binding required for lambda functions with capture and class member functions, you can only use std::function.
In fact, there are many other ways to implement callback functions, such as the following standard object-oriented implementation:
#include <iostream> // Define a standard callback interfaceclass ComputeFunc { public: virtual int compute(int x, int y) const = 0; }; // Implement the callback interfaceclass ComputeAdd : public ComputeFunc { public: int compute(int x, int y) const override { return x + y; } }; int compute3(int x, int y, const ComputeFunc& compute) { // Call interface method return (x, y); } // The call method is as followsint main() { ComputeAdd add_func; // Create a call instance int sum = compute3(3, 4, add_func); // Pass in the call instance}
The object-oriented approach is more flexible, because the object of this callback can have very complex behavior.
The above three methods have their own benefits, and they are determined based on the complexity, scalability and application scenarios of the functions you need to implement.
In addition, the parameters of these function types may be empty. Before calling, you should check whether it can be called, such as checking whether the function pointer is empty.
Summarize
The above is the entire content of this article. I hope that the content of this article has a certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.