2012-06-21 18 views
8

Suponga que tieneuna función superclase como parámetro de plantilla no nombretipo

struct A{ 
    void f(){} 
}; 


struct B:public A{ 
}; 


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

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

¿por qué

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

trabajo, pero

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

no, quejándose

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

?

(Call<A,&A::f> funciona con claridad)

De manera similar

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

da

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

Respuesta

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

obras porque conversión implícita de

void (A::*f)() 

a

void (B::*f)() 

se aplica.

4,11 (2)

A prvalue de tipo “puntero a miembro de B de tipo cv T”, donde B es un tipo de clase, se pueden convertir a un prvalue de tipo “puntero a miembro de D de tipo cv T”, donde D es una clase derivada (Cláusula 10) de B.

sin embargo, la norma no permite ninguna conversión para puntero a función miembro en argumentos de plantilla, excepto de la conversión nullptr_t:

14.3.2

Para un parámetro de plantilla sin tipo de tipo puntero a función de miembro, si el argumento de plantilla es de tipo std :: nullptr_t, se aplica la conversión de puntero de miembro nulo (4.11); de lo contrario, no se aplican las conversiones . Si el argumento de plantilla representa un conjunto de funciones de miembro sobrecargadas, la función de miembro coincidente se selecciona del conjunto (13.4).

+0

¡Genial, gracias! Por cierto, ¿también sabe que const void (B :: * f)() = &B::f; no se acepta también? –

+0

@Fabio: void (B :: * const f)() = & B :: f funciona bien :). const void (B :: * f)() significa puntero al método que devuelve el valor de vacío const, por lo que las firmas de los métodos no coinciden en este último caso. – user396672

+0

lo siento, tienes razón, ¡gracias! –

0

Los estados de error exactamente lo que está mal, y void (A::*)()void (B::*)() diferentes tipos.

Si bien en este caso parece que debería ser fácil de hacer, el caso general se vuelve mucho más complejo. Considere lo que sucedería si A tuviera varias funciones virtuales y B tuviera herencia múltiple. Los indicadores para las funciones miembro son bestias muy complejas porque tienen que dar cuenta de ese tipo de cosas. Echar un vistazo a http://blogs.msdn.com/b/oldnewthing/archive/2004/02/09/70002.aspx

Usted podría cambiar a B:

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

Para que B::f() realmente existe. En este momento B::f() es un alias de A::f() que obviamente es de tipo void (A::*)() y no void (B::*)()

+0

Estoy totalmente de acuerdo con sus puntos. Mi pregunta es por qué es posible tener void (B :: * f)() = &B::f; entonces. Dicho sea de paso, const void (B :: * f)() = &B::f; no se acepta –

+0

@FabioDallaLibera Existen diferentes reglas: los parámetros de plantilla deben ser del mismo tipo mientras que el operador de asignación puede usar tipos convertibles. No esperarías 'std :: vector a; std :: vector b; a = b; 'funcionar simplemente porque un' char' se puede convertir implícitamente en 'int'. – IronMensan

Cuestiones relacionadas