I saw it in the project code that the company said std::function,std::bind,std::placeholders Three C++11 The characteristics of , After understanding , It's useful to find out , Record it here . It seems that these three features are generally used together , So we talked about it together .

      All three characteristics are present functional Defined in file , Therefore, it is necessary to use it “#include<functional>”

1.  Basic introduction

      Here are three features .

1.1 std::function
template< class > class function; /* undefined */ template< class R, class...
Args > class function<R(Args...)>;
      The official statement is :“Class template std::function is a general-purpose polymorphic
function wrapper. Instances of std::function can store, copy, and invoke any
Callable target -- functions, lambda expressions, bind expressions, or other
function objects, as well as pointers to member functions and pointers to data
members.”

     
That is to say, it is a general wrapper of polymorphic function , Its instances can be stored , Assign values and call any target that can be called : function ,lambda expression ,bind Expression or other function object , There are also pointers to member functions and to data members .
     
  The stored callable object is std::fucntion Our goal , The target is stored in the std::function after , By calling std::function Object to call the stored object indirectly , Generally, the stored objects are functions . If std::function The target is not included , It's an empty target , Call something like this std::function Will be thrown std::bad_function_call abnormal .
        What's actually used for storage std::function It's also an object , When storing, you need to specify the return value and parameters of the storage target .

1.2 std::bind
template< class F, class... Args > /*unspecified*/ bind( F&& f, Args&&... args
); template< class R, class F, class... Args > /*unspecified*/ bind( F&& f,
Args&&... args );
        The official statement is :“The function template bind generates a forwarding call wrapper
for f. Calling this wrapper is equivalent to invoking f with some of its
arguments bound to args.”

       
std::bind Is a function template , It's like a function adapter , Accepts a callable object , A new callable object is generated to fit the original parameter list , The template returns a std::function object . With this function, we can also adjust the order of parameters and set the specified parameters to a fixed value .

1.3 std::placeholders

       
placeholder , By viewing functional The file can be seen c++11 Among them 29 Place holder , namely _1~_29, Generally speaking, it's writing std::placeholders::_1 It's like this . The function of a placeholder is to represent a parameter ,std::placeholders::_1 It means std::bind Got it std::function Object is called , The first parameter passed in , and std::placeholders::_2 Is the second parameter .

     
  We adjust std::placeholders::_x stay std::bind The order of time , You can adjust the order of parameters . in addition , We can also be in the std::bind Don't use it when it's time std::placeholders::_x, And write it directly as a fixed value , This way, the child calls std::function Stored objects , The parameter of the corresponding position will be a fixed value .

2.  example

          The following code is an example of use , Pay attention to compile with “-std=c++11”, Otherwise, it cannot be compiled .
#include <functional> #include <iostream> int g_Minus(int i, int j) {
std::cout << "in g_Minus: " << i << " " << j << std::endl; return i - j; }
class tClass { public: void setID(int id) { id_ = id; } int getID() { return
id_; } static int wrongID() { return 100; } int id_ = 5; private: void
psetID(int id) { id_ = id; } }; struct Add { int operator()(int i, int j) {
return i + j; } }; template <class T> struct Sub { T operator()(T i, T j) {
return i - j; } }; template <class T> T Mul(T i, T j) { return i * j; } int
main() { // example 1. Ordinary function std::function<int(int,int)> f = g_Minus; int ret =
f(1,2); std::cout << ret << std::endl; // example 2. Static member functions of classes
std::function<int(void)> f2 = &tClass::wrongID; ret = f2(); // example 3. Class ordinary member function
tClass c1; std::function<void(int)> f3 = std::bind(&tClass::setID, &c1,
std::placeholders::_1); std::cout << c1.getID() << std::endl; f3(10); std::cout
<< c1.getID() << std::endl; // example 4. Function object std::function<int(int,int)> f4 =
Add(); // Bound to a function object ,Add() Will return an anonymous function object std::cout << f4(5, 8) << std::endl; //
example 5. Template function object std::function<float(float, float)> f5 = Sub<float>(); std::cout <<
f5(11.55, 3.33) << std::endl; // example 6. template function std::function<double(double,double)>
f6 = Mul<double>; std::cout << f6(11.55, 3.33) << std::endl; // example 7. Class ordinary member function
std::function<void(tClass &, int)> f9 = &tClass::setID; f9(c1, 150); std::cout
<< c1.getID() << std::endl; // example 8. Fit to a parameter , And set the second parameter to a fixed value 5
std::function<int(int)> f8 = std::bind(g_Minus, std::placeholders::_1, 5);
std::cout << f8(6) << std::endl; // example 9. Adjust parameter order std::function<int(int, int)>
f10 = std::bind(g_Minus, std::placeholders::_2, std::placeholders::_1);
std::cout << f10(8, 3) << std::endl; // example 10. Class member std::function<int(tClass
const&)> n1 = &tClass::id_; std::cout << n1(c1) << std::endl; return 1; }
       
  Combined with the above description , Basically, you can learn how to store all kinds of targets to std::function How to use it , There are no examples here lambda Expression , Because of me C++11 Of lambda I'm not familiar with expressions yet .

       
  in addition , From the example above 3 And examples 7 We can find that there are two ways to store ordinary members of a class , One is to use std::bind, One is not used , Causes the actual call std::function It's different when it comes to objects . But the actual calling procedure is the same , It's just that std::bind It acts as an adapter , Already setID The first parameter of the default is c1 I've got your address , Instead of bind What's more, we need to import tClass object . So why can we do this ? In fact, when we call a member function of a class , The compiled assembly code defaults to the class object as the first parameter this Pointer , The second parameter starts with the parameter we passed in , So the way we call it here , Is closer to the underlying call mode .

        From the example above 3,8,9 We can learn about placeholders std::placeholder The role of .

  3.  Why use it std::function

     
  I saw it at first std::function When we use it , I think of function pointers , Why do you have a pointer , We need to make a plan std::function What about it ? Later, we can learn from the information , There are two main points :1. 
Function pointers are unsafe , When called through a function pointer , There is no parameter checking , The return value is not checked . Because it's through cast , In this way, it is easy to have problems ;2. 
A function pointer cannot be bound to a member function of a class , Errors will be reported when compiling . The most important thing is the first point !
#include <iostream> void test(int param) { int ret = 0; for (int i = 0; i <
param; ++i) { ret += i; } ret >>= 16; ret |= (ret << 16); } typedef bool
(*PFUNC)(char args, int arg2); int main(void) { PFUNC func; func = (PFUNC)test;
if (func('a', 111)) { std::cout << "good" << std::endl; } else { std::cout <<
"bad" << std::endl; } return 0; }
       
  In the function above ,test Only one parameter is required without a return value , But I can force its address to a function pointer that takes two arguments and returns a boolean type , It's easy to go wrong . You cannot bind to a member function of a class , I tested the compilation and failed , So there's no code .

       
  Through the above example , It's easy to find problems with function pointers , and std::function Because it is a class template , Therefore, the parameter and return value will be checked when the parameter is passed , This is much safer than a function pointer .

4.  Application examples

       
  And because std::function Can be bound to a member function of a class , We can use the same interface , In the same class, the member functions of many other classes are called at the same time . In your company , This feature is used to implement the event registration and processing mechanism . Part of the code is as follows :
typedef std::function<int (R&, const E&)> CbFuncType; class EMgr { public:
void Register(uint32_t event_type, CbFuncType func); void Raise(Role& role,
Event event); private: std::map<uint32_t, std::vector<CbFuncType> >
event_hendle_map_; }; typedef Singleton<EMgr> EMgrSingleton; #define
REGISTER(event, func) \ EMgrSingleton::get_mutable_instance().Register(event, \
std::bind(&func, this, std::placeholders::_1, std::placeholders::_2));
       
The declaration of the executing function of the same event in all classes that need to be registered is the same . Pass the registration std::bind Generate an anonymous object and the corresponding event id Bind together , Add to event map Corresponding in vector Zhongqu . Note that you also need to pass in the registered class object , above this Used in various classes REGISTER Macro will be replaced with the corresponding class object .

       
  When the event is triggered , Just traverse the corresponding event id Of vector, And one by one std::function Just the object . It's convenient to use the execution function of all the events in the class ( Is a member function ) It's preserved .

        It's very convenient to use , It's really a nice feature .

Technology