C++ std::function() as a function parameter entry detailed explanation

C++ std::function() as a function parameter entry detailed explanation

1. About std::function()

In the era of C language, we can use function pointers to pass a function as a parameter, so that we can implement the mechanism of callback function. After C++11, the std::function template class was introduced in the standard library. This template summarizes the concept of function pointer. A
function pointer can only point to one function, and the std::function object can represent any object that can be called, such as Say any object that can be called as a function.
When you create a function pointer, you must define the function signature (characterizing the input parameters of the function, return value and other information); similarly, when you create a std::function object, you must also specify what it is. Represents the function signature of the callable object. This can be achieved through the template parameter of std::function.
For example, if you want to define a std::function object func, this object can represent any callable object with the following function signature,

bool(const std::unique_ptr<Widget>)&,//used to compare two in C++11 const std::unique_ptr<Widget>&)//Function signature of std::unique_ptr<Widget> object

You can write like this,

std::function<bool(const std::unique_ptr<Widget>&, const std::unique_ptr<Widget>&)> func;

This is because the lambda expression produces a callable object, which is called a closure here and can be stored in the std::function object.
The definition of closure is a collection of a function and the non-local variables it refers to (variables not defined inside a lambda expression).

2. Use std::function as a function parameter

2.1 Passing parameters based on value-by-value

Refer to the following piece of code to implement a mechanism for registering callback functions.

#include <functional> void registerCallBack(std::function<void()>);

The input parameter std::function<void()> is a template class object, which can be initialized with a callable object whose function signature is void(); the above implementation is a call by value. Let's take a look at its calling process,

//Method (A) registerCallBack([=]{ ....//The implementation part of the callback function })

Here a lambda expression is used as the input parameter of the function. As mentioned earlier, the lambda expression generates an anonymous closure (closure). Based on this closure, a std::function<void()> object is constructed Then pass this object to the registerCallBack function by calling by value.

2.2 Passing parameters based on reference

Of course, we can also implement this registration function as follows. The input parameters are passed by const reference. The reference here must be const. This is because a temporary std::function() object is generated where the registerCallBack function is called. Rvalue, otherwise the compiler will report an error.

//Method (B) void registerCallBack(std::function<void()> const&)

The difference between the two lies in how to use this input parameter inside the registerCallBack function. If you simply call the std::func() class, then there is no problem with the two types, and it may be more efficient to use references; if the register function This std::func() needs to be saved internally and used for future use. Then there is no problem with method A directly saving, method B must make a copy, otherwise in method B, when the temporary object is destroyed, there may be a dangling reference problem.

2.3 std::function object saving in pass-by-value mode

If we want to save the passed function object inside the registerCallBack function, we can use the transfer operation std::move, which is more efficient.

class CallBackHolder { public: void registerCallBack(std::function<void()> func) { callback = std::move(func); } private: std::function<void()> callback; }

3. Class member functions as function parameters

The member functions of the class will have a hidden this pointer by default, so unlike ordinary functions, it can be directly used as a parameter.

3.1 Use std::bind() and std::function to achieve

std::function is a universal polymorphic function wrapper, its instances can be stored, copied and called any target that can be called: functions, lambda expressions/bind expressions or other function objects, as well as pointers and pointers to member functions Data member pointer;
std::bind accepts a function (or function object) and generates a reorganized function object;
look at the following example, classA provides a registration function to register a callback function

class classA { typedef std::function<void(int i)> callback_t; ... void registCb(callback_t func) {cbHandle = std::move(func);} private: callback_t cbHandle; };

Another class, classB, needs to register a member function of its own as a callback function to classA. Here, you can use the std::bind function to implement it.

class classB { public: classB(classA& cA) { cA.registCb(std::bind(&classB::handle, this, std::placeholders::_1)); } };
  • The this pointer of classB displayed in the bind function is passed as the first parameter to the callback function;
  • std::placeholders:_1 represents a placeholder, which is used to explicitly enter the parameters of the callback function;

There is a section in Effective Modern C++ that explains the std::bind method is cumbersome and sometimes has some limitations, so after the introduction of lambda expressions, you can use lambda expressions to replace std::bind to implement functions Callback registration.

3.2 Implementation using lambda expressions

Using lambda expressions can simplify this process. Refer to the following piece of code, classB registers a member function as a callback function to classA, and classA saves this callback function (std::function object) to member variables for later use use,

#include <iostream> #include <functional> #include <memory> class classA { typedef std::function<void(int i)> callback_t; public: classA() {} ~classA() {} void handle(int i) { std::cout << "classA::handle" << std::endl; cbHandle(i); } void registCb(callback_t func) {cbHandle = std::move(func);} private: callback_t cbHandle; }; class classB { public: classB(classA& cA) { cA.registCb([this](int i){classB::handle(i);}); } ~classB() {} void handle(int i) { std::cout << "classB, handle message" << i << std::endl; } }; int main() { classA testa; classB testb(testa); testa.handle(10); }
  • The this pointer of classB is captured in the lambda expression
  • Use std::move to save the function object to classA


Transfer from: https://www.jianshu.com/p/c4c84b073413

Reference : https://blog.csdn.net/p942005405/article/details/84755007