1. 什么是std::function
std::function其實就是一個類模板,含有c的函數(shù)指針概念。
類模版std::function是一種通用、多態(tài)的函數(shù)封裝。std::function的實例可以對任何可以調(diào)用的目標實體進行存儲、復(fù)制、和調(diào)用操作,這些目標實體包括普通函數(shù)、Lambda表達式、函數(shù)指針、以及其它函數(shù)對象等。
簡要來說:std::function是將所有可調(diào)用的實體封裝起來,形成了一個新的std::function對象,用戶在使用的時候不需要再去一一調(diào)用實體,只需要使用新的std::function來調(diào)用各實體
如下,std::function作為回調(diào)函數(shù)使用,它可以調(diào)用任何有兩個int形參的返回值為int的對象
#include <iostream>
#include <functional>
#include <list>
using namespace std;
// 傳統(tǒng)C函數(shù)
int c_function(int a, int b)
{
return a + b;
}
// 函數(shù)對象
class Functor
{
public:
int operator()(int a, int b)
{
return a + b;
}
};
int main(int argc, char** argv)
{
Functor functor;
std::list<std::function<int(int, int)>> callables;
callables.push_back(c_function);
callables.push_back(functor);
callables.push_back([](int x, int y)->int{
return x + y;
});
for (const auto& e : callables)
{
cout << e(3, 4) << endl;
}
}
2.std::function作為形參
又因為std::function兼容c的函數(shù)指針,所以它還包含函數(shù)指針應(yīng)有的特性。但是它又和函數(shù)指針不同,函數(shù)指針只能指向一個函數(shù),而std::function可以指向任何可以被當做函數(shù)調(diào)用的對象
以下內(nèi)容來自
2.1 基于傳值的方式傳入?yún)?shù)
void registerCallBack(std::function<void()>);
2.2 基于引用的方式傳入?yún)?shù)
因為調(diào)用initWithFunction會產(chǎn)生臨時的std::function對象,屬于右值,必須使用const,不然會報錯
bool CallFunc::initWithFunction(const std::function<void()> &func)
{
_function = func;
return true;
}
2.3 std::function的保存
轉(zhuǎn)移操作std::move效率更高
class CallBackHolder
{
public:
void registerCallBack(std::function<void()> func)
{
callback = std::move(func);
}
private:
std::function<void()> callback;
}
3. 類的成員函數(shù)作為函數(shù)入?yún)?/h2>
3.1 使用std::bind()和std::function來實現(xiàn)
std::bind完成了實體和函數(shù)地址的綁定,因為它的參數(shù)里面既有對象指針,又有函數(shù)指針,從而制造了一個std::function,然后std::function只要能正確處理那個this指針,那就能完成正確地調(diào)用了
class RTree
{
public:
void searchOverlap(std::function<void(int)>f)
{
f(2);
}
};
class CallRtree
{
public:
virtual void searchRes(int a)
{
this->a = a;
}
void search()
{
RTree rtree;
std::function<void(int)>functional = std::bind(&CallRtree::searchRes, this, std::placeholders::_1);
//通常直接寫成 auto a =std::bind(....);
rtree.searchOverlap(functional);
int v = this->a;
}
private:
int a;
}
int _tmain(intargc, _TCHAR* argv[])
{
CallRtree caller;
caller.search();
}
3.2 使用lambda表達式實現(xiàn)(先不說了,以后復(fù)習(xí)了lamdba再來講)
2. 什么是std::bind
- 將函數(shù)、成員函數(shù)和閉包轉(zhuǎn)成function函數(shù)對象
- 將多元(n>1)函數(shù)轉(zhuǎn)成一元函數(shù)或者(n-1)元函數(shù)。
bind是一種機制,可以預(yù)先把指定的可調(diào)用的實體的某些參數(shù)綁定到已有的變量,產(chǎn)生一個新的可調(diào)用實體。
它作為一個通用函數(shù)適配器,接收一個可調(diào)用對象,生成一個新的可調(diào)用對象來適應(yīng)原對象的參數(shù)列表。
2.1 bind的使用方法
比如,存在一個這樣的函數(shù)check_size,因為這是一個二元函數(shù),當我們要將它作為find_if的參數(shù),會出錯。因為find_if只接受一元函數(shù),那么如何解決呢?
一個方法是Lambda表達式,還有一個方法就是使用std::bind
bool check_size(const string &s,string::size_type sz)
{
return s.size()>=sz;
}
下面這個bind的函數(shù)只有一個占位符,即只需要傳入一個參數(shù)。它將check_size的第二個參數(shù)綁定在sz上,sz的值就是check_size的第二個參數(shù)的值,而check_size第一個參數(shù)需要傳入
auto wc = find_if(words.begin(),words.end(),bind(check_size,_1,sz));
2.2 bind的參數(shù)問題
auto g = bind(f,a,b,_2,c,_1);
如果現(xiàn)在我們調(diào)用g(3,5),那么就相當于bind(f,a,b,5,c,3);
所以_1相當于傳遞的第一個參數(shù),_2相當于傳遞的第二個參數(shù)...以此類推。
需要注意:bind對于直接綁定的值,是以值傳遞的方式,對于用_1這類,是使用引用傳遞。bind的返回值是可以調(diào)用的實體,所以通常我們都會將它和function聯(lián)合在一起使用。
2.3 bind引用參數(shù)
有時候?qū)τ谟行┙壎ǖ膮?shù)我們希望以引用方式傳遞,或者說要綁定的參數(shù)無法拷貝。
比如ostream 流對象是無法拷貝的,那么我們希望將它傳遞給bind而不拷貝它,就需要使用ref。
ref返回一個對象,包含給定的引用,是可以拷貝的。
for_each(words.begin(),words.end,bind(print,ref(os),_1,' '));
2.4 std::bind綁定成員函數(shù)和靜態(tài)成員函數(shù)
對于成員函數(shù)的綁定,我們一定需要一個調(diào)用者,也就是類的實例!
需要注意的是,bind無法綁定重載函數(shù),因為當重載函數(shù)的參數(shù)個數(shù)不相同時,bind也失去了它的意義。
class Utils {
public:
Utils(const char* name) {
strcpy(_name, name);
}
void sayHello(const char* name) const {
std::cout << _name << " say: hello " << name << std::endl;
}
static int getId() {
return 10001;
}
int operator()(int i, int j, int k) const {
return i + j + k;
}
private:
char _name[32];
};
int main(void) {
// 綁定成員函數(shù)
Utils utils("Vicky");
auto sayHello = std::bind(&Utils::sayHello, utils/*調(diào)用者*/, std::placeholders::_1);
sayHello("Jack");
// 綁定靜態(tài)成員函數(shù)
auto getId = std::bind(&Utils::getId);
std::cout << getId() << std::endl;
}
2.5 bind與function搭配
在cocos2dx的源碼中,我們經(jīng)??梢钥吹絝unction作為函數(shù)形參,而bind作為實參傳入
bool Label::multilineTextWrap(std::function<int(const std::u16string&, int, int)> nextTokenLen)
{}
bool Label::multilineTextWrapByChar()
{
return multilineTextWrap(std::bind(getFirstCharLen, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
}