2009-09-17 12 views
6

Supongamos que tenemos la siguiente base y clases derivadas:¿Por qué las funciones temporales de miembro no se vinculan con el tipo correcto?

#include <string> 
#include <iostream> 

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

class Porsche : public Car { 
}; 

..y también la función siguiente plantilla:

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

¿Por qué esta compilación usando GCC:

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

... pero no esto?

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

Probado con g ++ 4.0.1 bajo MacOSX y ambos compilados bien, ambos con punteros virtuales y no virtuales cuando ambos punteros se refieren al mismo método en la misma clase. –

+0

¿De verdad? También compilé exactamente el mismo código en OSX. Se dio el siguiente error: test2.cpp: En función de 'Función (void (T :: *)(), nula (V :: *)()) [con T = coches, V = Porsche]': Test2.cpp: 25: instanciado desde aquí Test2.cpp: 17: error: operandos inválidos de los tipos 'void (Car :: *)()' y 'void (Porsche :: *)()' a binary 'operator = = ' –

Respuesta

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

ptr tiene tipo void (Porsche::*)(), pero &Porsche::Drive tiene escriba void (Car::*)() (debido a que el miembro se encuentra en Car, no en Porsche). Por lo tanto la función llamada compara estas dos punteros miembros con dichos tipos, y la norma dice

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 describe una conversión estándar implícita void (Base::*)()-void (Derived::*)(). Por lo tanto, la comparación encontraría el tipo común void (Porsche::*)(). Para un objeto de tipo Porsche, ambos punteros de miembro se referirían a la misma función (que es Car::Drive), por lo que la comparación arrojaría verdadero. El comeau web compiler sigue esta interpretación y compila su código.

+0

Dada su declaración, no entiendo por qué la conversión 'void (Porsche :: * ptr) (void) = & Porsche :: Drive' es válida. ¿No debería 'void (Car :: * ptr) (void) = & Car :: Drive' ser la única asignación válida en este caso? –

+0

Se garantiza que una clase derivada contiene los miembros de sus clases base. Es por eso que 'T Base :: *' convierte implícitamente a 'T Derived :: *' (es inversa a la conversión habitual de 'Derived *' a 'Base *'). –

+0

Según 4.11, ¿la siguiente declaración debería funcionar también? 'plantilla void Función (void (T :: * m1) (void), void (T :: * m2) (void))' –

1

con g ++ 4.0.1 los ejemplos suministrados por el compilarse bien, pero tiene problemas relacionados con las comparaciones de los punteros de función miembro de distintos tipos (incluso si el elemento de comparación es un método virtual.

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 
} 

Ahora, He probado lo mismo con el compilador en línea de comeau y el código completo compila bien. No puedo decirle de inmediato cuál es el comportamiento estándar. Tendré que buscar el estándar por un tiempo.

EDIT: Supongo que no, litb ya lo hizo.

Cuestiones relacionadas