Búsqueda de nombres en plantillas de C ++

Tengo un código C ++ que ya no se está comstackndo 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á el resultado de g ++

template_eg.cpp: In instantiation of 'void Special_List::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' are not found by unqualified lookup template_eg.cpp:18:25: note: use 'this->next' instead 

Entonces aquí está el código que genera el problema:

 template class List { public: void next(T*){ cout<<"Doing some stuff"<<endl; } }; template class Special_List: public List { public: void do_other_stuff(T* item){ next(item); } }; int main(int argc, char *argv[]) { Special_List b; int test_int = 3; b.do_other_stuff(&test_int); } 

No estoy tratando de descubrir cómo arreglar el código para que se vuelva a comstackr. Eso es simplemente una cuestión de cambiar el siguiente (elemento) a este-> siguiente (artículo) Estoy tratando de entender 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 toma una T * (puntero a tipo T) hacerlo dependiente del argumento de la plantilla. En mi propia redacción, ¿no debería el comstackdor (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, así 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 eso?

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 el primer paso, no hay nada que indique que el next depende de los argumentos de la plantilla, y por lo tanto, debe resolverse antes de la sustitución del tipo . Ahora, debido a que el tipo de base está modelado en el argumento de plantilla de su plantilla actual, el comstackdor no puede verlo (puede ser especializado para algunos tipos, y sin saber qué tipo de T estamos creando con la plantilla, no podemos saber qué especialización para usar, es decir, la base depende de T y estamos comprobando antes de saber T ).

El truco de agregar this-> convierte a next en un nombre dependiente, y eso a su vez significa que la búsqueda se retrasa hasta el segundo pase, donde se conoce a T , y porque se conoce a T , la List también se conoce y puede verse arriba en.


EDIT : un detalle importante que falta en la redacción de la respuesta anterior es que la búsqueda de la segunda fase (después de la sustitución del tipo) solo agregará funciones encontradas durante la búsqueda dependiente del argumento. Es decir, si la next fue 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

Necesita escribir this-> como:

 this->next(item); 

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

template_eg.cpp: 18: 25: nota: las declaraciones en la base dependiente 'List' no se encuentran por búsqueda no calificada
template_eg.cpp: 18: 25: nota: use 'this->next' lugar

Lea este artículo que explica la búsqueda de nombres en dos fases en C ++:

  • La temida búsqueda de nombres en dos fases

Si su clase base es una instancia de plantilla, entonces no hay manera de saber que lo next refiere a un nombre en la clase base; después de todo, el nombre ni siquiera necesita existir (¡piense en especializaciones!). Por lo tanto, debe afirmar al comstackdor que ahora es miembro de la clase diciendo this-> , o List::next , o anteponiendo using List::next; a su plantilla de clase derivada.