2009-09-17 54 views
6

假設我們有以下的基類和派生類:爲什麼不將成員函數臨時綁定到正確的類型?

#include <string> 
#include <iostream> 

class Car { 
public: 
    void Drive() { std::cout << "Baby, can I drive your car?" << std::endl; } 
}; 

class Porsche : public Car { 
}; 

..和還下面的模板功能:

template <typename T, typename V> 
void Function(void (T::*m1)(void), void (V::*m2)(void)) { 
    std::cout << (m1 == m2) << std::endl; 
} 

爲什麼這個編譯使用GCC:

int main(int argc, char** argv) { 
    void (Porsche::*ptr)(void) = &Porsche::Drive; 
    Function(ptr, ptr); 
    return 0; 
} 

...但不是這個?

int main(int argc, char** argv) { 
    void (Porsche::*ptr)(void) = &Porsche::Drive; 
    Function(&Porsche::Drive, ptr); 
    return 0; 
} 
+0

用g ++測試過4.0.1在MacOSX下編譯都很好,當兩個指針指向同一個類中的同一個方法時,它們都帶有非虛擬指針和虛擬指針。 – 2009-09-17 18:16:49

+0

真的嗎?我還在OSX下編譯了完全相同的代碼。它給出了以下錯誤: Test2.cpp:函數'void Function(void(T :: *)(),void(V :: *)())[with T = Car,V = Porsche]': Test2.cpp:25:從這裏實例化 Test2.cpp:17:錯誤:類型爲'void(Car :: *)()'和'void(Porsche :: *)()'的二元運算符= =' – 2009-09-17 19:10:13

回答

7
int main(int argc, char** argv) { 
    void (Porsche::*ptr)(void) = &Porsche::Drive; 
    Function(&Porsche::Drive, ptr); 
    return 0; 
} 

ptr具有類型void (Porsche::*)(),但&Porsche::Drive具有類型void (Car::*)()(因爲構件在Car發現,而不是在Porsche)。由此調用的函數使用的那些類型這兩個構件的指針進行比較,並在標準說

In addition, pointers to members can be compared, or a pointer to member and a null pointer constant. Pointer to member conversions (4.11) and qualification conversions (4.4) are performed to bring them to a common type. If one operand is a null pointer constant, the common type is the type of the other operand. Otherwise, the common type is a pointer to member type similar (4.4) to the type of one of the operands, with a cv-qualification signature (4.4) that is the union of the cv-qualification signatures of the operand types.

4.11描述的隱式轉換的標準從void (Base::*)()void (Derived::*)()。因此,比較將找到通用類型void (Porsche::*)()。對於類型爲Porsche的對象,兩個成員指針都會引用相同的函數(即Car::Drive) - 因此比較結果將爲真。 comeau web compiler遵循這個解釋並編譯你的代碼。

+0

鑑於你的陳述,我不明白爲什麼轉換'void(Porsche :: * ptr)(void)=&Porsche :: Drive'是有效的。在這種情況下,不應該將void(Car :: * ptr)(void)=&Car :: Drive'作爲唯一有效的賦值? – 2009-09-17 18:36:07

+0

派生類保證包含其基類的成員。這就是爲什麼'T Base :: *'隱式轉換爲'T Derived :: *'(這與'Derived *'到'Base *'的通常轉換相反)。 – 2009-09-17 18:40:37

+0

根據4.11,以下聲明也應該工作? 'template void Function(void(T :: * m1)(void),void(T :: * m2)(void))' – 2009-09-17 18:43:16

1

隨着克++ 4.0.1你提供編譯細的例子,但是它有一個處理與不同類型的成員函數指針的比較的問題(即使所比較的元件是一個虛擬方法。

struct base 
{ 
    void f() {} 
    virtual void g() {} 
}; 
struct derived : public base 
{ 
    void f() {} 
    virtual void g() {} 
}; 
template <typename T, typename U> 
bool cmp(void (T::*lhs)(), void (U::*rhs)()) { 
    return lhs == rhs; 
} 
int main() 
{ 
    void (base::*bp)() = &base::f; 
    void (base::*bvp)() = &base::g; 

    cmp(bp, &base::f); // compiles 
    cmp(bvp, &base::g); // compiles 

    void (derived::*dp)() = &derived::f; 
    void (derived::*dvp)() = &derived::g; 

    cmp(dp, &derived::f); // compiles 
    cmp(dvp, &derived::g); // compiles 

    cmp(bp, dp); // fails to compile 
    cmp(bvp, dvp); // fails to compile 
} 

現在,我已經用comeau在線編譯器測試過了,整個代碼都編譯好了,我不能馬上告訴你什麼是標準行爲,我將不得不瀏覽一下標準。已經做到了。

相關問題