template plantillas c++ templates language-lawyer argument-dependent-lookup

template - Búsqueda de nombres dependientes en la instanciación de plantillas C++



plantillas en c++ (3)

La búsqueda en la fase dos solo incluye reglas de búsqueda de nombres que no pueden aplicarse en la fase uno, por ejemplo, ADL. La búsqueda en dos fases es exactamente eso: algunos nombres se buscan en la fase uno, y algunos se buscan en la fase dos. Este tipo particular de nombre es un nombre de fase uno, ya que el compilador es perfectamente capaz de buscar foobar en el (los) espacio (s) de nombres de la función durante la fase uno.

Visual C ++ no implementa la búsqueda de nombres de dos fases.

Cuando intento compilar este código

// void foobar(int); template <class T> struct Foo { void bar(T t) { foobar(t); }; }; void foobar(int); template class Foo<int>;

con g ++ 4.8.2 me aparece el siguiente mensaje de error

foo.cc: In instantiation of ‘void Foo<T>::bar(T) [with T = int]’: foo.cc:10:16: required from here foo.cc:5:27: error: ‘foobar’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive] void bar(T t) { foobar(t); }; ^ foo.cc:8:6: note: ‘void foobar(int)’ declared here, later in the translation unit void foobar(int); ^

(Con clang 3.4 es casi lo mismo).

Primero, creo que el código es correcto y debería compilarse, ya que foobar es un nombre dependiente en la declaración de plantilla y debería buscarse solo en la fase dos cuando se crea una instancia de la plantilla. Cuando esto se hace en la última línea, ya se ha declarado ''foobar (int)''. El código se compila, por cierto, cuando elimino el comentario de la línea superior, pero ambas declaraciones son anteriores a la instanciación, por lo que esto no debería importar.

Segundo, el mensaje de error en sí me parece contradictorio. Dice "no se encontraron declaraciones en el momento de la instalación", que es foo.cc:10:16, y dice que se declaró "más tarde" en foo.cc:8:6. Por todo lo que sé sobre números y el idioma inglés, lo llamaría "antes" y no "más tarde".

Entonces, ¿es un error en gcc o me salió algo mal? Sin embargo, dado que esto me parece un patrón de uso común, no puedo creerlo.

Por cierto: cuando pruebo el segundo ejemplo de "Resolución de nombres para tipos dependientes" en MSDN ( http://msdn.microsoft.com/en-us/library/dx2zs2ee.aspx ) con g ++, el resultado es diferente de vc ++, que (No en general, pero en este caso específico) socavaría que esto sea un error en g ++.


Me parece correcto. Si bien la resolución de sobrecarga se realiza solo en la fase 2, en la fase 1 ya debe saber que foobar(t) es una expresión de llamada de función. Si foobar nombra un tipo, t ni siquiera sería un nombre dependiente.


tl; dr Foo<int> no invoca ninguna ADL, pero Foo<X> haría (donde X es un tipo de clase).

En primer lugar, en este código, foobar es un nombre dependiente debido a (C ++ 14 / N3936) [temp.dep]/1

En una expresión de la forma:

postfix-expression ( expression-list opt )

donde el postfix-expresión es un id no calificado, el id no calificado denota un nombre dependiente si [...]

  • cualquiera de las expresiones en la lista de expresiones es una expresión dependiente del tipo (14.6.2.2), o

y t es un nombre dependiente porque es parte de una declaración T t donde T es un parámetro de plantilla y, por lo tanto, un tipo dependiente.

Al pasar a la resolución de nombres dependientes, hay [temp.dep.res]/1 que introduce el hecho de que los nombres se pueden buscar tanto en el contexto de definición como en el contexto de creación de instancias, y define dónde está el contexto de creación de instancias. Lo he omitido por brevedad, pero en esta template class Foo<int>; ejemplo template class Foo<int>; Es el punto de instanciación.

El siguiente bit es [temp.dep.candidate]/1 :

Para una llamada a la función donde la expresión-posfijo es un nombre dependiente, las funciones candidatas se encuentran utilizando las reglas de búsqueda habituales (3.4.1, 3.4.2), excepto que:

  • Para la parte de la búsqueda que utiliza la búsqueda de nombre no calificado (3.4.1), solo se encuentran las declaraciones de función del contexto de definición de la plantilla.
  • Para la parte de la búsqueda que utiliza espacios de nombres asociados (3.4.2), solo se encuentran las declaraciones de función encontradas en el contexto de definición de la plantilla o en el contexto de creación de instancias de la plantilla.

Esas dos últimas partes son las "dos fases" de la búsqueda en dos fases. (Nota: esta sección cambió en el texto de C ++ 11 a C ++ 14, pero el efecto en el mismo).

En la primera fase, 3.4.1, no se encuentran nombres para foobar .

Así que nos movemos hacia la segunda fase. Los lugares reales donde se buscan los nombres como se describe en 3.4.2. El texto es largo, pero aquí están dos de las reglas relevantes:

  • Si T es un tipo fundamental, sus conjuntos de espacios de nombres y clases asociados están vacíos.

  • Si T es un tipo de clase (incluidas las uniones), sus clases asociadas son: la clase en sí misma; la clase de la que es miembro, en su caso; y sus clases básicas directas e indirectas. Sus espacios de nombres asociados son los espacios de nombres de cierre más internos de sus clases asociadas. [...]

Entonces, cuando creas una instancia de Foo<int> , la segunda fase de búsqueda no introduce ningún espacio de nombres adicional para buscar.

Sin embargo, si cambia su ejemplo para que tenga la struct X {}; y luego cambia int a X todas partes, entonces el código se compila. Esto se debe a la última viñeta: ADL para un argumento de tipo de clase busca en el espacio de nombres adjunto de esa clase (que ahora es el espacio de nombres global), sin embargo ADL para un argumento de tipo incorporado no busca en el espacio de nombres global.