2011-04-05 62 views
7

我最近對函子感到興奮,並且一直在使用它們。然後出現這種情況,我需要我的仿函數來執行兩個不同的操作,並且我想添加另一個方法到我的仿函數(而不是重載()運算符)。不管這是不是好的做法,我不確定(也許你可以告訴我),但它讓我想到了爲什麼我首先使用函子而不僅僅是物體。所以我的問題是:爲什麼C++函數對於具有命名方法的對象更可取?

重載()運算符有什麼特殊之處,或者它比使用正常的命名方法稍微有語法吸引力嗎?

更新:

首先,我知道爲什麼函子可優選作爲在其他問題解釋函數指針。我想知道爲什麼他們可以比使用命名方法的對象更可取。其次,關於何時我想要使用函數的另一個可能命名方法的示例:基本上我有兩個函數,一個用於計算稱爲圖分區的模塊性的東西 - compute_modularity(),另一個用於計算增益在分區compute_modularity_gain()的一些改變之後以模塊化。我認爲我可以將這些函數作爲相同仿函數的一部分傳遞給優化算法,並將增益作爲命名函數。我不只是將兩個函子傳遞給算法,是因爲我想強制compute_modularity_gain()僅與compute_modularity()結合使用,而不是另一個函數,例如compute_stability()(應該僅compute_stability_gain()使用。換句話說,增益功能必須緊密結合與其兄弟的功能。如果有另一種方式我可以強制執行此約束,那麼請讓我知道。

+0

描述使您希望添加其他方法的場景。 – 2011-04-05 00:45:59

+4

這似乎已被覆蓋:[C++ Functors - 及其使用。](http://stackoverflow.com/q/356950/478288) – chrisaycock 2011-04-05 00:46:19

+4

@chrisaycock:你應該只投票關閉*確切*重複。這個問題是相似的,但有一個質的不同方面:爲什麼不能使用正常的命名方法來實現函數,爲什麼他們只能實現一種方法? – 2011-04-05 00:52:13

回答

0

唯一特殊關於仿函數可以和函數一樣使用 但函子也可以通過構造函數注入信息

你也許還想看看std :: function(或者boost :: function,如果你的編譯器現在還不支持它),它可以用來適應具有匹配呼叫簽名的任何類型的對象。

std :: bind或boost :: bind允許您將具體參數與函數的參數相關聯,這允許與通過函數的構造函數傳遞它們相同的效果。你甚至可以使用bind來提供這個指向成員函數的指針,這樣它們就可以像一個普通的函數一樣被調用,而不需要明確地指定一個對象。

+0

「也可能通過它們的拷貝構造函數注入信息」< - 嗯,不是它們的拷貝構造函數。只是普通的構造函數。另外請記住,仿函數必須是[純函數](http://en.wikipedia.org/wiki/Pure_function) - 您不應該在STL仿函數中存儲狀態,因爲STL允許任意拷貝仿函數時間,因爲它想要的。 – 2011-04-05 01:36:42

+0

是的,不知道我怎麼最終鍵入複製構造函數,而意味着正常的構造函數,編輯。 ;○ – xDD 2011-04-05 01:53:11

1

函子的基本意圖是解耦知道如何從知道何時需要完成工作的代碼中執行某種工作的代碼(經典示例是將函數與UI按鈕相關聯)。

函數模型的一個小優點是普通的舊函數指針已經是函子了。不需要額外的工作來包裝它們。我認爲這是一個小的好處,因爲a)函數指針比直接調用函數稍微低效,b)我發現我幾乎總是需要將某種形式的狀態綁定到我要包裝的任何形式,即使它只是成員函數的指針this

一元接口的關鍵優勢在於它作爲仿函數的生產者和消費者的通用語言。你可以說,定義仿函數都有一個invoke()成員函數,但是其他一些人會決定在do()上標準化,而另一個人可能會去call()。所有這些解決方案都涉及更多的打字。

此外,單個「仿函數」上的多個成員函數從不被嚴格要求。如果某些代碼需要調用多個不同的操作,則可以簡單地傳遞多個函數。這提供了很好的靈活性,因爲操作可能是耦合的,或者它們可能完全不相關。

一個解耦的例子是一個哈希表,它需要一個相等比較器和一個哈希函數。在這種情況下,這兩個函數可能無關:將類的operator==()包裝爲相等,幷包裝一個自由函數來計算散列。

一個耦合的例子是一個UI組件,它發出幾個不同的事件。一個班級可能會對所有事件做出反應,或者不同的班級可能會對不同組別的事件做出反應。函子可以很容易地選擇任何一種模型,而需要一個定義所有組件事件的回調函數的單一「接口」則更加笨拙。如果單個對象想要以不同方式處理來自兩個組件的事件,則函子也會使它更容易,因爲您可以爲每個組件提供一組不同的函數包裹成員函數。

最後,將函數中的現有功能包裝在函數中是很好理解的,並且被諸如boost.bind之類的庫廣泛支持,而創建實現doX()doY()的丟棄類卻不是。另外,新標準增加了lambda表達式,大大簡化了函數的創建。

7

operator()過載的原因是讓函子具有與函數指針相同的調用語義 - 事實上,如果您願意,可以使用函數指針。

有幾個原因可以重載operator()而不是使用函數 - 但最重要的是編譯器在使用函數指針時很少優化掉間接函數調用,但它們幾乎總是會優化掉operator()電話 - 這就是爲什麼std::sort通常跳動std::qsort

這裏有一大堆複雜的原因,但它真正歸結爲大多數(沒有?)編譯器實現了刪除函數指針調用的優化,這在現代硬件上是昂貴的。

那麼情況出現,我需要我的仿函數來執行兩種不同的操作

然後,它不再是一個仿函數。要麼通過兩個函數來做你想要的,要麼定義一個template method類。 (你也可以使用mixins來實現C++中的模板方法模式,而無需運行時間開銷 - 但維基百科的文章沒有涉及到這一點)(另請注意:與C++模板不一樣,儘管如果你去了C++模板可能會涉及C++模板AOP路線)

相關問題