C++ 謂詞(predicate) 與 仿函數 ( functor (function object))


謂詞與函數對象

謂詞 predicate

C++ 標准定義謂詞如下:

The Predicate parameter is used whenever an algorithm expects a function object that when applied to the result of dereferencing the corresponding iterator returns a value testable as true. In other words, if an algorithm takes Predicate pred as its argument and first as its iterator argument, it should work correctly in the construct if (pred(*first)){...}. The function object pred shall not apply any non-constant function through the dereferenced iterator. This function object may be a pointer to function, or an object of a type with an appropriate function call operator.

換成中文就是:當一個算法需要一函數對象,去應用於解引用(deferencing)迭代器(deferencing 解釋: int p;將定義一個指向整數的指針,並p取消引用該指針,這意味着它將實際檢索p指向的數據。)的結果,謂詞參數返回一個testable值為true。換句話說,換句話說, 如果算法將謂詞 pred 作為其參數,first參數是迭代參數,則如果 (pred (* first)) {..} , 它應該在構造中正常工作。函數對象 pred 不應通過解引用迭代器應用於任何非定常(non-constant)函數。此函數對象可以是指向函數的指針, 也可以是具有適當函數調用運算符的類型的對象。

仿函數

functor(函數對象或函數)形式:object + ()
這包括正常函數,函數指針和 () 運算符(函數調用運算符)重載的類對象,即為其定義函數 operator()() 類。
有時我們可以在普通函數不起作用時使用函數對象。STL經常使用函數對象並提供幾個非常有用的函數對象。
函數對象是泛型編程的力量和純抽象概念的另一個例子。我們可以說任何行為都像函數對象是函數。因此,如果我們定義一個行為是函數的對象,它可以用作函數。

#include <iostream> 

struct absValue 
{ 
	float operator()(float f){ 
		return f> 0?f:-f; 
	} 
}; 

int main()
{ 
	using namespace std; 

	float f = -123.45; absValue aObj; 
	float abs_f = aObj(f); 
	cout <<“f =”<< f <<“abs_f =”<< abs_f << '\n'; 
	return 0; 
}

正如我們從定義中看到的那樣,即使 aObj 是一個對象而不是一個函數,我們也可以對該對象進行調用。效果是運行由對象absValue定義的重載調用操作符。運算符獲取浮點值並返回其絕對值。請注意,函數調用運算符必須聲明為成員函數。
因此,定義調用操作符的類類型的對象(如absValue對象)稱為函數對象。
一個函數的行為是可以通過使用括號和傳遞參數來調用。

func (arg1 , arg2);

如果我們希望對象以這種方式運行,我們必須通過使用括號和傳遞參數來調用它們。我們所要做的就是使用適當的參數類型定義operator() :

Class X {
public:
	// define "function call" operator
	return-value operator() (arguments) const;
	...
};

然后我們可以使用這個類的對象來表現我們可以調用的函數:

X fn; 
//... 
fn(arg1,arg2); //為函數對象fn調用operator()

此調用相當於:

fn.operator()(ARG1,ARG2); //為函數對象fn調用operator()

一個例子:

class Print { 
public:
	void operator()(int elem)const { 
		cout << elem <<“”; 
	} 
}; 

int main(){ 
	vector <int> vect; 
	for(int i = 1; i <10; ++ i){ 
		vect.push_back(i); 
	} 

	Print print_it; 
	for_each(vect.begin(),vect.end(),print_it); 
	cout << endl; 
	return 0; 
}

for_each(vect.begin(),vect.end(),print_it); 通常,第三個參數可以是仿函數,而不僅僅是常規函數。實際上,這提出了一個問題。我們如何聲明第三個參數?我們不能將它聲明為函數指針,因為函數指針指定了參數類型。因為容器幾乎可以包含任何類型,所以我們事先並不知道應該使用哪種特定類型。STL通過使用模板解決了這個問題。

template<class Iterator, class Function>
Function for_each(Iterator first, Iterator last, Function f) {
	while (first != last) {
		f(*first);	
		++first;
	}
	return f;
}

以下是Nicolai M. Josuttis在“The C ++ Standard Library”中列出的函數對象的一些優點。

  • 功能對象是“智能功能”。
    行為像指針的對象是智能指針。對於行為類似於函數的對象也是如此:它們可以是“智能函​​數”,因為它們可能具有超出operator()的能力。函數對象可以具有其他成員函數和屬性。這意味着函數對象具有狀態。

  • 每個函數對象都有自己的類型。
    普通函數只有在簽名不同時才有不同的類型。但是,當函數對象的簽名相同時,它們可以具有不同的類型。實際上,函數對象定義的每個函數行為都有自己的類型。這是使用模板進行泛型編程的重大改進,因為您可以將功能行為作為模板參數傳遞。

  • 函數對象通常比普通函數更快。
    模板的概念通常允許更好的優化,因為在編譯時定義了更多細節。因此,傳遞函數對象而不是普通函數通常會產生更好的性能。

STL改進了仿函數概念,如下所示:
generator : 是一種可以不帶參數調用的函數
一元函數:有一個參數的調用函數
二元函數:有兩個參數的調用函數

generator 可以被認為是一種 每次調用都返回 集合/序列的下一個值的函數/對象。

算法的特殊輔助函數是謂詞。謂詞是返回布爾值的函數(或者可以隱式轉換為bool的函數)。換句話說,謂詞類是一個仿函數類,其operator() 函數是謂詞,即其operator() 返回true或false。
謂詞在STL中被廣泛使用。標准關聯容器的比較函數是謂詞,謂詞函數通常作為參數傳遞給find_if等算法。根據其目的,謂詞是一元的或二元的。
一元函數返回一個布爾值是一元謂詞。
二元函數返回一個布爾值是二元謂詞。

謂詞的種類算法(algorithm) 的 謂詞(predicate) 詳解
函數 函數指針 lambda表達式 函數對象 庫定義的函數對象


免責聲明!

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



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