網站首頁 編程語言 正文
概念
function包裝器 也叫作適配器。C++中的function本質是一個類模板,也是一個包裝器。
那么我們來看看,我們為什么需要function呢?
包裝器定義式:
// 類模板原型如下
template <class T> function; // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板參數說明:
- Ret: 是被包裝的可調用對象的返回值類型
- Args... :是被包裝的可調用對象的形參類型
function包裝器可以對可調用對象進行包裝,包括函數指針、函數名、仿函數(函數對象)、lambda表達式
int f(int a, int b)
{
return a + b;
}
struct Functor
{
public:
int operator() (int a, int b)
{
return a + b;
}
};
class Plus
{
public:
//靜態 vs 非靜態
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
int main()
{
function<int(int, int)> f1 = f;
f1(1, 2);
function<int(int, int)> f2 = Functor();
f2(1, 2);
function<int(int, int)> f3 = Plus::plusi;
f3(1, 2);
//非靜態成員函數 要 + 對象
function<double(Plus, double, double)> f4 = &Plus::plusd;
f4(Plus(), 1.1, 2.2);
return 0;
}
注意事項:
取靜態成員函數的地址可以不用取地址運算符“&”,但取非靜態成員函數的地址必須使用取地址運算符“&”
非靜態成員函數在調用的時候要 + 對象,因為非靜態成員函數的第一個參數是隱藏this指針,所以在包裝時需要指明第一個形參的類型為類的類型。
類型統一
對于以下函數模板useF:
- 傳入該函數模板的第一個參數可以是任意的可調用對象,比如函數指針、仿函數、lambda表達式等
- useF中定義了靜態變量count,并在每次調用時將count的值和地址進行了打印,可判斷多次調用時調用的是否是同一個useF函數。
代碼如下:
template<class F, class T>
T useF(F f, T x)
{
static int count = 0;
cout << "count:" << ++count << endl;
cout << "count:" << &count << endl;
return f(x);
}
在不使用包裝器,直接傳入對象的時候,會實例化出三份
double f(double i)
{
return i / 2;
}
struct Functor
{
double operator()(double d)
{
return d / 3;
}
};
int main()
{
//函數指針
cout << useF(f, 11.11) << endl;
//仿函數
cout << useF(Functor(), 11.11) << endl;
//lambda表達式
cout << useF([](double d)->double{return d / 4; }, 11.11) << endl;
return 0;
}
輸出結果如下:
由于函數指針、仿函數、lambda表達式是不同的類型,因此useF函數會被實例化出三份,三次調用useF函數所打印count的地址也是不同的。
- 但實際這里其實沒有必要實例化出三份useF函數,因為三次調用useF函數時傳入的可調用對象雖然是不同類型的,但這三個可調用對象的返回值和形參類型都是相同的
- 這時就可以用包裝器分別對著三個可調用對象進行包裝,然后再用這三個包裝后的可調用對象來調用useF函數,這時就只會實例化出一份useF函數
- 根本原因就是因為包裝后,這三個可調用對象都是相同的function類型,因此最終只會實例化出一份useF函數,該函數的第一個模板參數的類型就是function類型的
包裝后代碼如下:
int main()
{
// 函數指針
function<double(double)> f1 = f;
cout << useF(f1, 11.11) << endl;
// 函數對象
function<double(double)> f2 = Functor();
cout << useF(f2, 11.11) << endl;
// lamber表達式
function<double(double)> f3 = [](double d)->double { return d / 4; };
cout << useF(f3, 11.11) << endl;
return 0;
}
例題:求解逆波蘭表達式
題目:
解題思路:
- 首先定義一個棧,依次遍歷所給字符串
- 遇到數字,直接入棧,遇到操作符,則從棧定拋出兩個數字進行對應的運算,并將運算后得到的結果壓入棧中
- 所給字符串遍歷完畢后,棧頂的數字就是逆波蘭表達式的計算結果
此處的包裝器:
- 建立各個運算符與其對應需要執行的函數之間的映射關系,當需要執行某一運算時就可以直接通過運算符找到對應的函數進行執行
- 當運算類型增加時,就只需要建立新增運算符與其對應函數之間的映射關系即可(其他代碼不用動)
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<long long> st;
map<string, function<long long(long long, long long)>> opfuncMap =
{
//自動構造pair ~ 初始化列表構造
{"+", [](long long a , long long b){return a + b;}},
{"-", [](long long a , long long b){return a - b;}},
{"*", [](long long a , long long b){return a * b;}},
{"/", [](long long a , long long b){return a / b;}},
};
for(auto& str : tokens)
{
if(opfuncMap.count(str))
{
//操作符 :出棧(先出右,再出左)
long long right = st.top();
st.pop();
long long left = st.top();
st.pop();
st.push(opfuncMap[str](left, right));
}
else
{
//操作數:入棧
st.push(stoll(str));
}
}
return st.top();
}
};
包裝器的意義
將可調用對象的類型進行統一,便于我們對其進行統一化管理。
包裝后明確了可調用對象的返回值和形參類型,更加方便使用者使用。
bind 包裝器
bind 是一種函數包裝器,也叫做適配器。它可以接受一個可調用對象,生成一個新的可調用對象來“適應”原對象的參數列表,C++ 中的 bind 本質還是一個函數模板
bind函數模板的原型如下:
template <class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);
template <class Ret, class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);
模板參數說明:
- fn:可調用對象
- args...:要綁定的參數列表:值或占位符
調用bind的一般形式
auto newCallable = bind(callable, arg_list);
callable:需要包裝的可調用對象
newCallable:生成的新的可調用對象
arg_list:逗號分隔的參數列表,對應給定的 callable 的參數,當調用 newCallable時,newCallable 會調用 callable,并傳給它 arg_list 中的參數
_1 _2 ... 是定義在placeholders命名空間中,代表綁定函數對象的形參;_1代表第一個形參,_2代表第二個形參 …
舉例:
using namespace placeholders;
int x = 2, y = 10;
Div(x, y);
auto bindFun1 = bind(Div, _1, _2);
bind 綁定固定參數
原本傳入的參數要求是要3個,現在只需要輸入兩個參數,因為綁定了固定的函數對象
using namespace placeholders;
class Sub
{
public:
int sub(int a, int b)
{
return a - b;
}
};
int main()
{
//function<int(Sub, int, int)> fsub = &Sub::sub;
function<int(int, int)> fsub = bind(&Sub::sub, Sub(), _1, _2);
}
想把Mul函數的第三個參數固定綁定為1.5,可以在綁定時將參數列表的placeholders::_3設置為1.5。比如:
int Mul(int a, int b, int rate)
{
return a * b * rate;
}
int main()
{
function<int(int, int)> fmul = bind(Mul, _1, _2, 1.5);
}
調整傳參順序
對于 Sub 類中的 sub 函數,因為 sub 的第一個參數是隱藏的 this 指針,如果想要在調用 sub 時不用對象進行調用,那么可以將 sub 的第一個參數固定綁定為一個 Sub 對象:
using namespace placeholders;
class Sub
{
public:
int sub(int a, int b)
{
return a - b;
}
};
int main()
{
//綁定固定參數
function<int(int, int)> func = bind(&Sub::sub, Sub(), _1, _2);
cout << func(1, 2) << endl;
return 0;
}
此時調用對象時就只需要傳入用于相減的兩個參數,因為在調用時會固定幫我們傳入一個匿名對象給 this 指針。
如果想要將 sub 的兩個參數順序交換,那么直接在綁定時將 _1 和_2 的位置交換一下就行了:
using namespace placeholders;
class Sub
{
public:
int sub(int a, int b)
{
return a - b;
}
};
int main()
{
//綁定固定參數
function<int(int, int)> func = bind(&Sub::sub, Sub(), _2, _1);
cout << func(1, 2) << endl;
return 0;
}
其原理:第一個參數會傳給_1,第二個參數會傳給 _2,因此可以在綁定時通過控制 _n 的位置,來控制第 n 個參數的傳遞位置
bind包裝器的意義
1.將一個函數的某些參數綁定為固定的值,讓我們在調用時可以不用傳遞某些參數。
2.可以對函數參數的順序進行靈活調整。
原文鏈接:https://blog.csdn.net/qq_42996461/article/details/128888581
相關推薦
- 2022-07-19 Python數據分析之NumPy常用函數使用詳解_python
- 2022-07-12 合理使用gateWay過濾器,實現Concroller自動注入用戶信息
- 2022-09-23 windows10本地搭建FTP服務器圖文教程_FTP服務器
- 2023-10-10 函數柯里化的簡單實現和應用
- 2022-09-05 基于React?Context實現一個簡單的狀態管理的示例代碼_React
- 2022-09-15 python安裝whl文件的實戰步驟_python
- 2023-10-16 Echart 數據更新了,X軸或者Y軸顯示不變化的問題
- 2022-07-31 pycharm終端解釋器與Python解釋器配置_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支