- 作者: 雪山肥魚
- 時間:20220226 21:52
- 目的: function
# 標(biāo)準(zhǔn)庫中的std::function
# 實(shí)現(xiàn)類似std::function的功能
標(biāo)準(zhǔn)庫中的 std::function
function 可調(diào)用對象包裝器
函數(shù)名(函數(shù)指針)谓罗,仿函數(shù),lambda表達(dá)式等等,都是可調(diào)用對象
#include <iostream>
#include <functional>
using namespace std;
void myfunc(int tmpvalue) {
cout << "myfunc 執(zhí)行了 tmpvalue = " << tmpvalue << endl;
}
void callObjFunc(function<void(int)> cobj) {
cobj(120);
}
int main(int argc, char **argv) {
callObjFunc(myfunc);
callObjFunc([](int tmpvalue) {
cout << "lambda tmpvalue= " << tmpvalue << endl;
});
return 0;
}
function 即 接受各種可調(diào)用對象。
實(shí)現(xiàn)類似 function 的功能
#include <iostream>
#include <functional>
using namespace std;
void myfunc(int tmpvalue) {
cout << "myfunc 執(zhí)行了 tmpvalue = " << tmpvalue << endl;
}
void callObjFunc(function<void(int)> cobj) {
cobj(120);
}
//4.引入可調(diào)用對象處理器
template <typename T, typename... Args>
class CFObjHandler {
public:
virtual T invoke(Args... args) const {
}
};
//5.需要傳給handler U(可調(diào)用類型) 增加子類模板
template <typename U, typename T, typename... Args>
class CFObjHandlerChild :public CFObjHandler<T, Args...>
{
public:
//構(gòu)造函數(shù),這里并不是萬能引用电谣,純右值引用
CFObjHandlerChild(U && tmpfuncobj) :functor(std::forward<U>(tmpfuncobj))
{
}
T invoke(Args... args) const {
return functor(std::forward<Args>(args)...);
}
private:
U functor;//U 是一個可調(diào)用變量類型秽梅,functor 用于保存可調(diào)用對象
};
//泛化版本 -- 僅聲明,因為用不到,但 有特化 必須先有泛化
template <typename T>
class CallFuncObj;
//特化版本
template<typename T, typename... Args>
class CallFuncObj<T(Args...)> {//1. T: 可調(diào)用對象的返回類型剿牺, Args...: 參數(shù)類型
//整個 T(Args) 是一個參數(shù)企垦, 與上面的 泛化只有一個 typename T統(tǒng)一
public:
//2. 增加構(gòu)造函數(shù)
template<typename U>
CallFuncObj(U && acobj) {//萬能引用,acobj 是一個可調(diào)用對象,需要保存下來,U 需要傳遞到CFObjHandler
//也需要把U(可調(diào)用類型) 傳給 CFObjHandler
//6. 增加相關(guān)代碼
handler = new CFObjHandlerChild<U, T, Args...>(std::forward<U>(acobj));
}
~CallFuncObj()
{
delete handler;
}
//3. 增加 opertor()
T operator() (Args... args) const {
return handler->invoke(std::forward<Args>(args)...);//保持原始實(shí)參的左值性和 右值性
}
private:
CFObjHandler<T, Args...> * handler;//可調(diào)用對象處理起 這是父類型
};
void callObjFunc2(CallFuncObj<void(int)> cobj){
cobj(120);//調(diào)用額是 CallFuncObj 的 opertor()晒来,所以需要重載()
}
int main(int argc, char **argv) {
callObjFunc2(myfunc);
CallFuncObj<void(int)> f1 = myfunc;
f1(1200);
return 0;
}
- 關(guān)于特化的補(bǔ)充钞诡,如注釋所說,在特化中湃崩,在意的是 特化<參數(shù)個數(shù)A> 與 template<參數(shù)個數(shù)B> 泛化荧降, A 與 B 的統(tǒng)一。
- 增加模板構(gòu)造函數(shù)攒读,參數(shù)為萬能引
用 - 重載 opertor()
- 增加可調(diào)用對象處理器
- .需要傳給handler U(可調(diào)用類型) 增加子類模板
- 在構(gòu)造函數(shù)中朵诫,增加相關(guān)代碼
解釋:為何子類CFObjHandlerChild 的構(gòu)造函數(shù) 參數(shù)為 U && 右值。
- 首先避免拷貝薄扁,用U 也是完全可以的剪返。
- 如果傳入的是 lambda表達(dá)式,修改成左值是不行的邓梅。
分析調(diào)用流程:
CallFuncObj<void(int)> f1 = myfunc
- 執(zhí)行構(gòu)造函數(shù)模板
template <typename U>
CallFuncObj(U && acobj) {
handler = new CFObjHandlerChild<U, T, Args...>(std::forward<U>(acobj))
}
這里U 的類型是void(&)(int) 是左值引用類型脱盲。foward后 依舊是 左值類型。
- 進(jìn)入 child 的構(gòu)造函數(shù)
CFObjHandlerChild(U && tmpfuncobj): functor(std::foward<U>(tmpfuncobj)) {
}
此時 用用到引用折疊規(guī)則日缨,U &钱反,如果任意一個引用為左值引用,那么結(jié)果就為左值引用匣距。
傳進(jìn)來的是 void(&)(int) 此時 U& 依舊為 void(&)(int)
左值-》 左值 沒問題面哥。
依舊是折疊規(guī)則,如果參數(shù)為 U && 毅待, 左值void(&)(int) 進(jìn)來幢竹,這兩個其中有一個 是 左值引用,則 U && 輸出結(jié)果 依舊為 void(&)(int)左值引用恩静。
所以 這里如果是&& 左值引用和右值引用都沒問題焕毫。
只是左值引用 lambda 表達(dá)式 就不能編譯通過了。