2012-06-21 36 views
8

假設你有傳遞一個超類的功能非類型名稱的模板參數

struct A{ 
    void f(){} 
}; 


struct B:public A{ 
}; 


template<typename C,void (C::*f)()> 
struct Call{ 

    void operator()(C* c){ 
    (c->*f)(); 
    } 
}; 

爲什麼

int main(){ 
    void (B::*f)()=&B::f;  
} 

工作,但

Call<B,&B::f> a; 

不抱怨

could not convert template argument ‘&A::f’ to ‘void (B::*)() 

Call<A,&A::f>明確工作)

以類似的方式

const void (B::*f)()=&B::f; 

cannot convert ‘void (A::*)()’ to ‘const void (B::*)()’ in initialization 

回答

4
void (B::*f)()=&B::f; 

作品,因爲隱式轉換應用。

4.11(2)

類型的prvalue「指針類型CV的T B的構件」,其中B是一個類型,可被轉化爲一個 prvalue類型的「指針構件型品種的T d」,其中d是B的派生類(第10節)

然而,標準不允許成員函數指針在模板參數任何轉化除了nullptr_t轉換:

14.3。2

對於類型指針的非類型模板參數成員函數,如果模板-參數是 類型的std :: nullptr_t的,空構件指針轉換(4.11)被施加;否則,不適用轉換 。如果template-argument代表一組過載的成員函數,則從集合(13.4)中選擇匹配成員函數 。

+0

太好了,謝謝!順便說一句,你也知道const void(B :: * f)()= &B::f;也不被接受嗎? –

+0

@Fabio:void(B :: * const f)()=&B :: f工作正常:)。 const void(B :: * f)()表示指向返回const void值的方法的指針,因此方法的簽名在後一種情況下不匹配。 – user396672

+0

對不起,你是對的,謝謝! –

0

的錯誤狀態究竟什麼是錯的,void (A::*)()void (B::*)()是不同的類型。

雖然在這種情況下,似乎應該很容易做到,一般情況下變得更加複雜。考慮如果A具有多個虛擬功能並且B具有多重繼承會發生什麼情況。指向成員函數是非常複雜的野獸,因爲他們必須考慮這種事情。看看http://blogs.msdn.com/b/oldnewthing/archive/2004/02/09/70002.aspx

你可以改變B到:

struct B:public A{ 
    void f() { A::f(); } 
}; 

這樣B::f()確實存在。眼下B::f()A::f()別名這顯然是void (A::*)()型的,而不是從

void (A::*f)() 

void (B::*f)() 

void (B::*)()

+0

我完全同意你的觀點。我的奇蹟是爲什麼它有可能有void(B :: * f)()= &B::f;那麼。 順便提一句,const void(B :: * f)()= &B::f; 也不被接受 –

+0

@FabioDallaLibera有不同的規則:模板參數必須是相同類型,而賦值運算符可以使用可轉換類型。你不會指望'std :: vector a; std :: vector b; a = b;'因爲'char'可以隱式轉換爲'int'。 – IronMensan

相關問題