๐Foreword
This article learns about wrappers in C++11! ! !
๐1. Wrapper
๐1.1, function wrapper
function wrappers are also called adapters. A function in C++ is essentially a class template and a wrapper
Test = func(x);
-
What could the above func be?
-
func may be a function name? function pointer? Function object (functor object)? Or a lamber expression object?
-
These are all callable types! Such a rich type, if passed to the template, may lead to inefficiency of the template!
namespace FC { // Wrapper chapter: A class template may instantiate multiple copies of the same code at the same time, and different types call the same version of the function, resulting in low efficiency // This type may be a function pointer, functor, lambda expression, etc.... template <typename F, typename T> T useF(F f, T t) { static int Count = 0; cout << "Count: " << ++Count << endl; cout << "Count address: " << &Count << endl; return f(t); } double f(double x) { return x / 2; } struct Func { double operator()(double x) { return x / 3; } }; void Test() { // function pointer cout << useF(f, 1.2) << endl; // functor (function object) cout << useF(Func(), 1.2) << endl; // lambda expression cout << useF([](double x)->double { return x / 4; }, 1.2); } }
Through the above program verification, we will find that the useF function template instantiates three different codes
The wrapper can solve the above problem very well:
https://legacy.cplusplus.com/reference/functional/function/?kw=function
// The class template prototype is as follows template <class T> function; // undefined template <class Ret, class... Args> class function<Ret(Args...)>;
Template parameter description:
-
The std::function class template is defined in the functional header file
-
Ret: the return type of the called function
-
Args...: list of arguments to the called function
// The method of use is as follows: #include <functional> int f(int a, int b) { return a + b; } struct Functor { public: int operator() (int a, int b) { return a + b; } }; int main() { // function name (function pointer) std::function<int(int, int)> func1 = f; cout << func1(1, 2) << endl; // function object std::function<int(int, int)> func2 = Functor(); cout << func2(1, 2) << endl; // lamber expression std::function<int(int, int)> func3 = [](const int a, const int b) { return a + b; }; cout << func3(1, 2) << endl; }
You can also wrap class static member functions and non-static member functions:
class T { public: static int Ti(int x, int y) { return x + y; } int Td(int x, int y) { return x - y; } }; void Test() { // &T::Ti/T().Ti -- static member function, does not contain hidden this pointer std::function<int(int, int)> f4 = T().Ti; cout << f4(1, 2) << endl; // The member function contains a hidden this pointer, and the formal parameter needs to define an additional type std::function<int(T, int, int)> f5 = &T::Td; cout << f5(T(), 10, 5) << endl; }
Notice:
-
When wrapping a static member in a class, you can do not take the address of its function name (T::Ti), or you can directly call the assignment anonymously (T().Ti)
-
However, when wrapping member functions in a class, because a this pointer is hidden in the member functions, we need to display a class when defining parameters, otherwise an error will be reported
-
When wrapping the member function, the rvalue can only be the name of the member function that takes the address, and it may be that the member function can only be called through it.
With the wrapper, how to solve the problem of inefficient templates and multiple instantiations?
namespace FC { // Wrapper chapter: A class template may instantiate multiple copies of the same code at the same time, and different types call the same version of the function, resulting in low efficiency // This type may be a function pointer, functor, lambda expression, etc.... template <typename F, typename T> T useF(F f, T t) { static int Count = 0; cout << "Count: " << ++Count << endl; cout << "Count address: " << &Count << endl; return f(t); } double f(double x) { return x / 2; } struct Func { double operator()(double x) { return x / 3; } }; void Test() { std::function<double(double)> f1 = f; cout << useF(f1, 1.2) << endl; std::function<double(double)> f2 = Func(); cout << useF(f2, 1.2) << endl; std::function<double(double)> f3 = [](double x)->double { return x / 4; }; cout << useF(f3, 1.2) << endl; } }
Some other scenarios for wrappers:
https://leetcode.cn/problems/evaluate-reverse-polish-notation/submissions/
// How to play after using the wrapper class Solution { public: int evalRPN(vector<string>& tokens) { stack<int> st; map<string, function<int(int, int)>> opFuncMap = { { "+", [](int i, int j) {return i + j; } }, { "-", [](int i, int j) {return i - j; } }, { "*", [](int i, int j) {return i * j; } }, { "/", [](int i, int j) {return i / j; } } }; for (auto& str : tokens) { if (opFuncMap.find(str) != opFuncMap.end()) { int right = st.top(); st.pop(); int left = st.top(); st.pop(); st.push(opFuncMap[str](left, right)); } else { // 1,atoi itoa // 2,sprintf scanf // 3,stoi to_string C++11 st.push(stoi(str)); } } return st.top(); } };
๐2,bind
-
The std::bind function is defined in the header file and is a function template. It is like a function wrapper (adapter) that accepts a callable object and generates a new callable object to "fit" the original object. parameter list of
-
In general, we can use it to take a function fn that originally received N parameters, and return a new function that receives M parameters (M can be greater than N, but it does not make sense to do so) by binding some parameters.
-
At the same time, the use of std::bind function can also achieve parameter order adjustment and other operations
// The prototype is as follows: template <class Fn, class... Args> /* unspecified */ bind (Fn&& fn, Args&&... args); // with return type (2) template <class Ret, class Fn, class... Args> /* unspecified */ bind (Fn&& fn, Args&&... args);
The bind function can be thought of as a generic function adapter that takes a callable object and generates a new callable object to "fit" the original object's parameter list
The general form of calling bind: auto newCallable = bind(callable,arg_list);
-
Among them, newCallable itself is a callable object, and arg_list is a comma-separated list of parameters corresponding to the parameters of the given callable. When we call newCallable, newCallable will call callable and pass it the parameters in arg_list
-
The arguments in arg_list may contain names of the form _n, where n is an integer, and these arguments are "placeholders" representing the arguments to newCallable that occupy the "positions" of the arguments passed to newCallable.
-
The value n represents the position of the parameter in the generated callable: _1 is the first parameter of newCallable, _2 is the second parameter, and so on
-
bind can also change the order of the parameter list of the original object, for example: _2 is the first parameter of newCallable, _1 is the second parameter, then when passing parameters, it must be passed in reverse.
// Example of use int Plus(int a, int b) { return a + b; } class Sub { public: int sub(int a, int b) { return a - b; } }; int main() { //Indicates that the plus parameter of the bound function is specified by the first and second parameters of calling func1 respectively std::function<int(int, int)> func1 = std::bind(Plus, placeholders::_1,placeholders::_2); //auto func1 = std::bind(Plus, placeholders::_1, placeholders::_2); //The type of func2 is function<void(int, int, int)> which is the same type as func1 //Represents the first and second of the binding function plus: 1, 2 auto func2 = std::bind(Plus, 1, 2); cout << func1(1, 2) << endl; cout << func2() << endl; Sub s; // bind member function std::function<int(int, int)> func3 = std::bind(&Sub::sub, s, placeholders::_1, placeholders::_2); // Parameter swap order std::function<int(int, int)> func4 = std::bind(&Sub::sub, s, placeholders::_2, placeholders::_1); cout << func3(1, 2) << endl; cout << func4(1, 2) << endl; return 0; }
It can also be used like this:
namespace Bind { // Wrapper chapter: A class template may instantiate multiple copies of the same code at the same time, and different types call the same version of the function, resulting in low efficiency // This type may be a function pointer, functor, lambda expression, class member function, etc.... int f(int a, int b) { return a + b; } struct Functor { public: int operator() (int a, int b) { return a + b; } }; class T { public: int Ti(int x, int y) { return x + y; } }; void Test() { map<string, std::function<int(int, int)>> m { { "function pointer", f }, { "functor", Functor() }, { "member function", std::bind(&T::Ti, T(), placeholders::_1, placeholders::_2) } }; // [] returns the second(function itself) in pair<string, function>, you can directly find the corresponding key through [] and call it cout << m["function pointer"](1, 2) << endl; cout << m["functor"](10, 20) << endl; cout << m["member function"](100, 200) << endl; } }