函數調用運算符


如果類重載了函數調用運算符,則可以像使用函數一樣使用該類的對象,因為這樣的類同時也能存儲狀態,所以與普通函數相比它們更加靈活。

struct absInt{
	int operator()(int val) const{
		return val < 0 ? -val : val;
	}
};

上面的類只定義了一種操作:函數調用運算符,它負責接受一個int類型的形參,然后返回該實參的絕對值。

int i = -42;
absInt absObj;
int ui = absObj(i);	//i被傳遞給absObj.operator()

即使 absObj是一個對象而非函數,也能調用該對象,調用對象實際上是在運行重載的調用運算符。

函數調用運算符必須是成員函數,一個類可以定義多個不同版本的調用運算符,相互之間在參數數量和參數類型上應該有所區別。

如果類定義了調用運算符,則該類的對象稱作函數對象,因為可以調用這種對象,所以這些對象的行為像函數一樣。

含有狀態的函數對象類

函數對象除了 operator() 之外也可以包含其他成員。

class PrintString{
public:
	PrintString(ostream &o = cout,char c = ' '):os(o),sep(c){ }
	void operator()(const string &s)const{os<<s<<sep;}
private:
	ostream &os;
	char sep;
};

使用:

PrintString printer;
printer(s);		//cout中打印s,后面跟一個空格
PrintString errors(cerr,'\n');
errors(s);		//cerr中打印s,后面跟一個換行符

函數對象通常是作為泛型算法的實參:

for_each(vs.begin(),vs.end(),PrintString(cerr,"\n"));

lambda 是函數對象

編寫了一個 lambda 后,編譯器將該表達式翻譯成一個未命名類的未命名對象。

stable_sort(words.begin(),words.end(),[](const string &a,const string &b)
{return a.size() < b.size();});

其行為類似下面這個類的一個未命名對象:

class ShorterString{
public:
	bool operator()(const string &a,const string &b)
	{return a.size() < b.size();}
};

使用上面的類重寫 stable_sort :

stable_sort(words.begin(),words.end(),ShorterString());

表示 lambda 及相應捕獲行為的類

auto wc = find_if(words.begin(),words.end(),[sz](const string &a)
	{return a.size() > = sz;})

該 lambda 表達式產生的類將形如:

class SizeComp
{
	SizeComp(size_t n):sz(n) { }
	bool operator()(const string &s)const {return s.size() >= sz;}
private:
	size_t sz;
};

auto wc = find_if(words.begin(),words.end(),SizeComp(sz))

標准庫定義的函數對象

標准庫定義了一組表示算術運算符、關系運算符和邏輯運算符的類,每個類分別定義了一個執行命名操作的調用運算符。

plus<int> intAdd;	//可執行int加法的函數對
negate<int> intNegate;	//可對int值取反的函數對象
int sum = intAdd(10,20);	//sum=30
sum = intAdd(10,intNegate(10));	//sum=0

定義在 functional 頭文件中的函數對象:

在算法中使用標准庫函數對象

// 傳入一個臨時函數對象用於執行兩個 string 對象的比較運算
sort(svec.begin(),svec.end(),greater<string>());

標准庫規定其函數對象對於指針同樣適用:

vector<string *> nameTable; //指針的vector
//錯誤:nameTable 中的指針彼此之間沒有任何關系,所以 < 將產生未定義的行為
sort(nameTable.begin(),nameTable.end(),[](string *a,string *b){return a < b;}) ;
//正確,標准庫規定指針的 less 定義是良好的
sort(nameTable.begin(),nameTable.end(),less<string*>());

可調用對象與 function

C++ 中的可調用對象包括:函數、函數指針、lambda 表達式、bind 創建的對象以及重載了函數調用運算符的類。

可調用對象也有類型,labmda 有自己唯一的未命名類型,函數及函數指針的類型由其返回值類型和實參類型決定。兩個不同類型的可調用對象卻可能共享同一種調用形式,調用形式指明了返回類型以及傳遞給調用的實參類型,一種調用形式對應一個函數類型:

int (int,int)	//是一個函數類型,它接受兩個int,返回一個int

不同的類型可能具有相同類型的調用方式

int add(int i,int j){return i + j;}
auto mod = [](int i,int j){return i % j;}
struct divide{
	int operator()(int denminator,int divisor){
		return denminator / divisor;
	}
};


盡管這些可調用對象對其參數執行了不同的算術運算,盡管它們的類型各不相同,但是共享一種調用形式:

int (int,int)

構建實現不同運算的函數表:

map<string,int(*)(int,int)> binops;
binops.insert({"+",add});

binops.insert({"%",mod});	//錯誤,mod不是函數指針

標准庫 function 類型

function 定義在 functional 頭文件中:

function 是一個模板,創建具體的 function 類型時需要提供額外的信息。

function<int(int,int)>

function<int(int,int)> f1 = add;		//函數指針
function<int(int,int)> f2 = divide;		//函數對象類的對象
function<int(int,int)> f3 = [](int i,int j){return i * j;}		//lambda

//調用
cout<<f1(4,2)<<endl;
cout<<f2(4,2)<<endl;
cout<<f3(4,2)<<endl;

使用 function 重新定義上面的函數表:

map<string,function<int(int,int)>> binops = 
{
	{"+",add},		//函數指針
	{"-",std::minus<int>()},		//標准庫函數對象
	{"/",divide},		//自定義函數對象
	{"*",[](int i,int j){return i * j;}},			//未命名lambda表達式
	{"%",mod},				//命名lambda表達式
};

調用:

binops["+"](10,5);
binops["-"](10,5);
binops["/"](10,5);
binops["*"](10,5);
binops["%"](10,5);

重載的函數與 function

不能直接將重載函數的名字存入 function 類型的對象中。

int add(int i,int j){return i + j;}
Sales_data add(const Sales_data&,const Sales_data&);
map<string,funciton<int(int,int)>> binops;
binops.insert({"+",add});		//錯誤,不能區分是哪個add 

解決上面問題的有效途徑是存儲函數指針而非函數名字:

int (*fp) (int,int) = add;
binops.insert("add",fp);	//正確,fp指向正確的add版本

同樣,也可以使用 lambda 來指定希望使用的 add 版本:

binops.insert({"+"},[](int a,in b){return add(a,b);});


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM