2010-04-08 196 views
4

我使用定義的接口庫:成員函數指針

template<class desttype> 
void connect(desttype* pclass, void (desttype::*pmemfun)()); 

,我有一個小的層次

class base { 
    void foo(); 
}; 

class derived: public base { ... }; 

derived一個成員函數,我想致電

connect(this, &derived::foo); 

但似乎&derived::foo實際上是一個成員函數指針的base; GCC吐出

error: no matching function for call to ‘connect(derived* const&, void (base::* const&)())’ 

我可以明確地鑄造thisbase *解決這個問題;但爲什麼編譯器不能匹配desttype = base的調用(因爲derived *可以隱式轉換爲base *)?

另外,爲什麼&derived::foo不是成員函數指針derived

+0

是否有真的很好理由使用指向成員函數的指針,而不是僅僅在基礎中聲明(純?)虛函數,並在需要時調用它? – 2010-04-08 18:01:40

+0

@Jerry,你是什麼意思? – 2010-04-08 18:13:06

+0

@傑裏棺材:當有 - 有。調用純虛函數的唯一方法是通過該函數的硬編碼名稱。當硬編碼特定函數不可接受時,使用指向成員的指針。這一定是這種情況之一。 – AnT 2010-04-08 18:15:29

回答

7

首先,當你做&class::member時,結果的類型總是基於成員實際聲明的類。這就是在C++中多麼單一的&

其次,代碼不編譯,因爲模板參數推導失敗。從第一個參數推導出desttype = derived,而從第二個參數推導出desttype = base。這是編譯失敗的原因。 C++中的模板參數推導規則不考慮this可以轉換爲base *類型的事實。此外,可以爭辯說,代替將this轉換爲base *類型,正確的方法是將&derived::foo從指針到基本成員轉換爲指向派生成員類型。兩種方法同樣可行(見下文)。

第三,在C構件指針++服從禁忌方差,這意味着一個指向基類成員可隱式轉換爲一個指針指向一個派生類成員的規則。在你的情況,你需要做的是通過顯式指定參數,以幫助編譯通過模板參數推導得到的,和代碼應編譯

connect<derived>(this, &derived::foo); 

上面應該編譯,因爲禁忌方差&derived::foo指針,儘管它是指向base成員的指針。或者你可以做

connect<base>(this, &derived::foo); 

這也應該編譯,因爲協方差指針this

你也可以對實際參數(如你在問題中提到的)進行明確的轉換以獲得演繹歧義,但在我看來,在這種情況下,顯式指定的模板參數看起來更好。

+0

@AndreyT,謝謝,我沒有想到明確指定模板參數。至於我的第二個問題,我認爲我根據你的解釋把它拼湊在一起,但我建議結合你的「第一」和「第三」 - 我的猜測(儘管我可能是錯的)是*理由*成員函數指針的類型基於它聲明的類中的成員函數指針是逆變的,而不是協變的。如果'derived :: foo'在'derived'中返回一個成員函數,它將無法在'base *'上被調用。 – 2010-04-08 18:18:58

+1

有時候你只需要有人把概念上的詞語解釋出來,並解釋他們所代表的東西......我喜歡這樣做的男人!非常好的解釋。 – 2010-04-09 07:41:04

0

成員函數指針在C++中有很多特質,各種編譯器在工作方式上有不一致之處。道格Clugston的文章,"Member Function Pointers and the Fastest Possible C++ Delegates",是他們的工作(不工作),一個非常漂亮的概述:

與派生類打交道時

, 也有一些驚喜。例如, 下面的代碼將編譯上的MSVC如果你 完整保留意見:

class SomeClass { 
public: 
    virtual void some_member_func(int x, char *p) { 
     printf("In SomeClass"); }; 
}; 

class DerivedClass : public SomeClass { 
public: 
// If you uncomment the next line, the code at line (*) will fail! 

// virtual void some_member_func(int x, char *p) { printf("In DerivedClass"); }; 

}; 

int main() { 
    // Declare a member function pointer for SomeClass 

    typedef void (SomeClass::*SomeClassMFP)(int, char*); 
    SomeClassMFP my_memfunc_ptr; 
    my_memfunc_ptr = &DerivedClass::some_member_func; // ---- line (*) 
} 

奇怪的是, &DerivedClass::some_member_funcSomeClass類的 成員函數指針。它不是 DerivedClass的成員! (一些編譯器的行爲 略有不同:例如,對於 數字火星C++, &DerivedClass::some_member_func是 在這種情況下,不確定的。)但是,如果 DerivedClass覆蓋 some_member_func,代碼將無法 編譯,因爲 &DerivedClass::some_member_func已 現在變成類DerivedClass的成員函數指針 !

0

這是模板參數推導的問題,如果模板參數不明確說明在調用網站,則編譯器不會嘗試做自動轉換。

來解決這個問題的最好辦法,以我的經驗,正在申報兩個模板參數的函數:

template<typename Y, typename T> 
void connect(Y * pclass, void (T::*pmemfun)()); 

在這種情況下,編譯器能夠愉快地自動實例爲您

void connect<derived, base>(derived * pclass, void (base::*pmemfun)()); 

該解決方案,而且是完全安全的,因爲從轉換得出*立足*將完成內部連接(在這裏我假設你正在呼籲pclass - > * pmemfun())

+0

不幸的是,我不能(至少沒有過分的努力)改變方法的簽名。 – 2010-04-08 20:26:57

+0

在這種情況下,剩下的唯一解決方案就是顯式指定模板參數,正如AndreyT所說。 – 2010-04-08 20:49:08