2012-05-17 10 views
32

Tengo un código C++ que ya no se está compilando sin la opción -permissive. Es un código de propiedad que no puedo compartir, pero creo que he podido extraer un caso de prueba simple que demuestra el problema. Aquí está la salida desde g ++Búsqueda de nombres en plantillas C++

template_eg.cpp: In instantiation of 'void Special_List<T>::do_other_stuff(T*) [with T = int]': 
template_eg.cpp:27:35: required from here 
template_eg.cpp:18:25: error: 'next' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive] 
template_eg.cpp:18:25: note: declarations in dependent base 'List<int>' are not found by unqualified lookup 
template_eg.cpp:18:25: note: use 'this->next' instead 

Así que aquí está el código de la genera el problema:

template<class T> class List 
{ 
     public: 
     void next(T*){ 
      cout<<"Doing some stuff"<<endl; 
     }  
}; 

template<class T> class Special_List: public List<T> 
{ 
    public: 
     void do_other_stuff(T* item){ 
       next(item); 
     }  
}; 


int main(int argc, char *argv[]) 
{ 
    Special_List<int> b; 
    int test_int = 3; 
    b.do_other_stuff(&test_int); 
} 

No estoy tratando de averiguar cómo solucionar el código para que se compile de nuevo. Eso es simplemente una cuestión de cambiar el siguiente (artículo) a este-> siguiente (artículo) Estoy tratando de comprender mejor por qué este cambio es necesario. Encontré una explicación en esta página: http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html Si bien esa explicación fue útil, todavía tengo algunas preguntas. ¿No debería el hecho de que mi función tome una T * (puntero a tipo T) hacerlo dependiente del argumento de la plantilla. En mi propia redacción, ¿no debería el compilador (gcc 4.7) ser capaz de descubrir que la función next() está en la lista de la clase base? ¿Por qué es necesario anteponer esto-> frente a cada llamada? Me he dado cuenta de que clang 3.1 muestra el mismo comportamiento, por lo que supongo que hay algún requisito en el estándar de C++ que requiere este comportamiento. ¿Alguien podría proporcionar una justificación para ello?

Respuesta

50

El El problema es que las plantillas se procesan en dos pasos (de acuerdo con el estándar, VS lo contrario). En el primer paso, antes de la sustitución del tipo, todo lo que no depende de los argumentos de la plantilla se busca y se comprueba. Los nombres dependientes luego se dejan resolver en el segundo pase, una vez que el tipo ha sido sustituido.

Ahora, en la primera pasada no hay nada que indique que next depende de argumentos de plantilla, y por lo tanto hay que resolver antes de tipo de sustitución. Ahora, debido a que el tipo de base está modelado en el argumento de plantilla de su plantilla actual, el compilador no puede verlo (puede ser especializado para algunos tipos, y sin saber qué tipo de T estamos creando una instancia de la plantilla, no podemos saber qué especialización para usar, es decir, la base depende en T y estamos revisando antes de saber T).

El truco de añadir this-> convierte next en un nombre dependiente, y que a su vez significa que las operaciones de búsqueda se retrasa hasta que el segundo paso, donde T se conoce, y porque T se sabe, List<T> también es conocida y puede consultarse dentro.


EDITAR: Un detalle importante que falta en la redacción de la respuesta anterior es que la segunda fase de consulta (después del tipo de sustitución) sólo agregará funciones que se encuentran durante la búsqueda depende argumento. Es decir, si next fuera una función libre en un espacio de nombres asociado con T se encontraría, pero es un miembro en la base, que no es visible para ADL en T.

+3

+1. Breve y concisa explicación. – Nawaz

+0

¡Gracias! Entonces, si entiendo correctamente, los argumentos a continuación no son relevantes. El hecho de que next() tome un argumento dependiente, ¿no hace que next() sea dependiente? – Avatar33

+0

@ Avatar33: 'next()' es dependiente, pero la búsqueda de la segunda fase solo agregará resultados de ADL, que no incluyen miembros de la base. Quizás deba volver a visitar la forma de la respuesta ... –

5

Si su clase base es una instancia de plantilla, entonces no hay manera de saber que next se refiere a un nombre en la clase base - después de todo, el nombre no tiene siquiera existe (pensar especializaciones)! Por lo tanto, usted tiene que afirmar al compilador que next es realmente un miembro de la clase diciendo this->, o List<T>::next, o anteponiendo using List<T>::next; a su plantilla de clase derivada.

+0

Esta última técnica 'using' es muy útil en el caso de las declaraciones' using'/'typedef' en la clase base, para las cuales no puede usar' this-> '. Puede usar explícitamente 'typename Base :: MyType' siempre que se refiera al tipo, o más convenientemente puede declarar' using typename Base :: MyType; 'en la clase derivada. – Jeremy

8

tiene que escribir this-> como:

this->next(item); 

Aquí se requiere this-> parte porque next() es un miembro heredado de base de la plantilla, y si usted lee el mensaje de error con cuidado, se sugiere allí mismo:

template_eg.cpp: 18: 25: nota: las declaraciones en la base depende 'List<int>' no se encuentran por las operaciones de búsqueda no calificado
template_eg.cpp: 18: 25: nota: uso 'this->next' lugar

Lee este artículo que ha explicado búsqueda de nombre de dos fases en C++: