2012-04-20 9 views
16
#include <iostream> 

void foo() 
{ 
    std::cout << "global foo()" << std::endl; 
} 

struct A { 
    void foo() 
    { 
     std::cout << "A::foo()" << std::endl; 
    } 
}; 

struct B : public A { 
    void call() 
    { 
     foo(); 
    } 
}; 

int main(int argc, char **argv) 
{ 
    B b; 
    b.call(); 
    return 0; 
} 

Esto da expected result:¿Orden de búsqueda del alcance del símbolo de C++ diferente para la clase de plantilla y sin plantilla?

A::foo() 

Sin embargo después de cambiar dos líneas (clase B de la plantilla):

#include <iostream> 

void foo() 
{ 
    std::cout << "global foo()" << std::endl; 
} 

struct A { 
    void foo() 
    { 
     std::cout << "A::foo()" << std::endl; 
    } 
}; 

template <typename T> // change here 
struct B : public T { 
    void call() 
    { 
     foo(); 
    } 
}; 

int main(int argc, char **argv) 
{ 
    B<A> b; // and here 
    b.call(); 
    return 0; 
} 

consigo unexpected result:

global foo() 

Y el uso de this-> es no es una opción ya que estoy tratando de crear comió un mecanismo de "retroceso".

+0

Interesante de hecho. –

+0

Podría 'call() {T :: foo(); } 'estar bien para usar? Funciona. – chris

+0

¿Qué tal si llamamos 'T :: foo();' desde 'B :: call'? – mfontanini

Respuesta

16

Lo que obtienes es un resultado esperado. Esto se denomina "búsqueda de nombre de dos fases" en el estándar de C++.

nombres dentro de las plantillas se dividen en dos tipos:

dependientes - Nombres que dependen de los parámetros de plantilla, pero no se declaran dentro de la plantilla.

No dependiente - nombres que no dependen de los parámetros de la plantilla, más el nombre de la plantilla misma y los nombres declarados dentro de ella.

Cuando el compilador intenta resolver algún nombre en el código, primero decide si el nombre es dependiente o no, y el proceso de resolución se deriva de esta distinción. Mientras que los nombres no dependientes se resuelven "normalmente": cuando se define la plantilla, la resolución para los nombres dependientes se produce en el momento de la instanciación de la plantilla.

foo(); en B::call en su ejemplo es un nombre no dependiente, por lo que se resuelve en global foo() en el punto de definición de la plantilla.

+0

¿Cómo funciona eso con el comentario de Jagannath "Funciona bien VS 11 Beta. Llamadas A :: foo()."? Sé que MS no es lo mejor para validar ninguna norma. También cualquier referencia al estándar C++ (03) sería agradable. ¿Y sabes cómo hacer que se comporte como yo quiero? – elmo

+3

@elmo: Microsoft C++ no implementa la búsqueda de nombre de dos fases correctamente. Ver [esta pregunta] (http://stackoverflow.com/questions/6273176). –

+0

@elmo: Para que sea su forma de trabajo: plantilla estructura B: T pública { llamada void() { T :: foo(); // Añadir T :: al código }} ; – CppLearner

0

Tiene que decirle específicamente que use el método de clase T.

template <typename T> 
struct B : public T { 
    void call() 
    { 
     T::foo(); 
    } 
}; 


Pero en cuanto a un mecanismo de reserva, se puede comprobar esta pregunta: Is it possible to write a template to check for a function's existence?

utilizando un fracaso La sustitución no es un error (SFINAE), se puede comprobar para un método foo en T, y luego ejecute el método apropiado.

+0

Consulte su comentario y la última línea de esta pregunta. – Jagannath

+0

De nuevo: la última línea en mi pregunta dice claramente que no funcionaría: http://ideone.com/YuoIH – elmo

+0

@elmo: ¿por qué excepto que funciona cuando la estructura '' C'' no tiene '' método foo''? http://ideone.com/WcLQE – fogbit

3

La respuesta aceptada explica por qué ve ese comportamiento, pero no cómo lograr el comportamiento de "repliegue" que desea. Esto se puede hacer usando SFINAE, introduciendo un par de sobrecargas de plantillas miembro, una de las cuales solo existirá si la clase base tiene una función miembro llamada foo.

template <typename T> 
struct B : T { 
    template <void (T::*)()> struct has_mem_fn {}; 

    template <typename U> void call(has_mem_fn<&U::foo>*) {this->foo();} 
    template <typename U> void call(...) {foo();} 

    void call() {call<T>(0);} 
}; 

struct X {}; 

int main() 
{ 
    B<A> ba; 
    ba.call(); // A::foo() 

    B<X> bx; 
    bx.call(); // global foo() 
} 

ACTUALIZACIÓN: Acabo de notar sus comentarios en otra respuesta, en el que dice que está consciente de este método, pero no puedo usarlo debido a tener que soportar compiladores disfuncionales. En ese caso, me temo que lo que quieres es probablemente imposible.

Cuestiones relacionadas