2009-09-17 4 views
6

, dass wir die folgenden Basisklassen und abgeleiteten Klassen haben:Warum binden Temporäre von Elementfunktionen nicht an den richtigen Typ? Angenommen

#include <string> 
#include <iostream> 

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

class Porsche : public Car { 
}; 

..und auch die folgenden Template-Funktion:

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

Warum ist das Kompilieren mit GCC:

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

... aber nicht das?

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

Getestet mit g ++ 4.0.1 unter MacOSX und beide kompiliert, beide mit nicht-virtuellen und mit virtuellen Zeigern, wenn beide Zeiger auf die gleiche Methode in der gleichen Klasse verweisen. –

+0

Wirklich? Ich habe auch den exakt gleichen Code unter OSX kompiliert. Es gab folgenden Fehler: Test2.cpp: In Funktion 'void Funktion (void (T :: *)(), void (V :: *)()) [mit T = Auto, V = Porsche]': Test2.cpp: 25: von hier aus instanziiert Test2.cpp: 17: Fehler: ungültige Operanden der Typen 'void (Car :: *)()' und 'void (Porsche :: *)()' zu binär 'operator = = ' –

Antwort

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

ptr hat void (Porsche::*)() Typ, aber &Porsche::Drive hat void (Car::*)() Typ (wird, da das Mitglied in Car, nicht in Porsche gefunden). So bezeichnete die Funktion vergleicht diese beiden Mitglieder Zeiger mit diesen Typen, und der Standard sagt

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 eine implizite Standard-Konvertierung void (Base::*)()-void (Derived::*)() beschreibt. Somit würde der Vergleich den üblichen Typ void (Porsche::*)() finden. Bei einem Objekt vom Typ Porsche würden sich beide Elementzeiger auf die gleiche Funktion beziehen (also Car::Drive), so dass der Vergleich wahr ergibt. Die comeau web compiler folgt dieser Interpretation und kompiliert Ihren Code.

+0

Angesichts Ihrer Aussage, ich verstehe nicht, warum die Konvertierung 'void (Porsche :: * ptr) (void) = & Porsche :: Drive' gültig ist. Sollte in diesem Fall nicht die einzige ungültige Aufgabe sein (Auto :: * ptr) (void) = & Auto :: Fahren? –

+0

Eine abgeleitete Klasse enthält garantiert die Mitglieder ihrer Basisklassen. Das ist der Grund, warum 'T Base :: *' implizit in 'T Derived :: *' konvertiert wird (es ist umgekehrt zur üblichen Umwandlung von 'Derived *' in 'Base *'). –

+0

Laut 4.11 sollte die folgende Deklaration auch funktionieren? 'Vorlage void Funktion (void (T :: * m1) (void), void (T :: * m2) (void))' –

1

mit g ++ 4.0.1 die Beispiele, die Sie in Ordnung liefern kompilieren, aber es hat Probleme mit Vergleichen der Mitgliedsfunktionszeiger von verschiedenen Arten zu tun (auch wenn das verglichene Element eine virtuelle Methode ist.

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 
} 

Jetzt, Ich habe das gleiche mit cameau Online-Compiler getestet und der ganze Code kompiliert gut. Ich kann Ihnen nicht sofort sagen, was das Standardverhalten ist. Ich werde den Standard für eine Weile durchsuchen müssen.

EDIT: Ich denke nicht, litb schon gemacht

Verwandte Themen