c++ - GCC, Clang e IBM no están de acuerdo sobre cómo realizar una búsqueda de nombres dependiente del parámetro de la plantilla. ¿Cuál es la correcta?
templates argument-dependent-lookup (1)
Todos son correctos. No realmente, sigue leyendo ...
template<class T> void g(T a) {
f(123);
h(a);
}
Aquí, f
es un nombre no dependiente, pero h
es un nombre dependiente de acuerdo con 14.6.2 / 1. f
es levantado
Los nombres no dependientes utilizados en una definición de plantilla se encuentran utilizando la búsqueda de nombres habitual y se unen al punto en que se usan.
f
es levantado de inmediato y obligado a void f(double)
, la única f
visible en ese punto.
De acuerdo con 14.6.4.1, el punto de instanciación de void g<int>(int)
es inmediatamente después de la definición de void i()
, donde se usa.
[..] De lo contrario, el punto de instanciación para tal especialización sigue inmediatamente a la declaración o definición del alcance del espacio de nombres que se refiere a la especialización.
Esto significa que las fuentes para resolver el nombre dependiente son declaraciones visibles en la definición de la template<class T> void g(T a)
y "declaraciones de espacios de nombres asociados con los tipos de los argumentos de la función, tanto del contexto de instanciación (14.6.4.1) y del contexto de definición "(14.6.4).
Sin embargo, dado que int
es un tipo fundamental, el conjunto de espacios de nombres asociados está vacío (3.4.2) (no, ni siquiera se incluye el espacio de nombres global) y, de acuerdo con 14.6.4.2, solo se realiza una búsqueda utilizando los espacios de nombres asociados que pueden usar El contexto de creación de instancias de plantilla, la búsqueda normal de nombres no calificados solo puede usar lo que está visible en el contexto de definición de la plantilla. Esto confirma lo que se dijo en 14.6.4.
Ahora, el punto extra. 14.6.4.2 continúa diciendo:
Si la llamada estuviera mal formada o encontrara una mejor coincidencia, la búsqueda dentro de los espacios de nombres asociados consideraría todas las declaraciones de funciones con enlace externo introducidas en esos espacios de nombres en todas las unidades de traducción, no solo considerando las declaraciones encontradas en la definición de plantilla y plantilla. En los contextos de instanciación, entonces el programa tiene un comportamiento indefinido.
La llamada está mal formada porque la búsqueda falla (la parte sobre los espacios de nombres asociados no se aplica aquí), por lo que el comportamiento es explícitamente indefinido, por lo que cualquier cosa podría suceder. Por lo tanto, ninguno de los comportamientos vistos muestra una no conformidad con el estándar aunque, para mí, el diagnóstico de Clang parece ser el que más se ajusta al estándar.
(Todas las referencias ISO / IEC 14882: 2011.)
Considere este ejemplo que encontré en el sitio web de IBM:
#include <iostream>
using namespace std;
void f(double) { cout << "Function f(double)" << endl; }
template<class T> void g(T a) {
f(123);
h(a);
}
void f(int) { cout << "Function f(int)" << endl; }
void h(double) { cout << "Function h(double)" << endl; }
void i() {
extern void h(int);
g<int>(234);
}
void h(int) { cout << "Function h(int)" << endl; }
int main(void) {
i();
}
¿Qué se imprimirá?
La documentación de IBM de la que adapté este ejemplo, disponible here , dice que se imprimirá:
Function f(double) Function h(double)
La razón para esto es que la búsqueda de nombres dependiente del parámetro de la plantilla se realiza justo antes de la creación de instancias de
i()
, por lo que encuentrah(double)
pero noh(int)
.Cuando lo compilo usando GCC 4.4.1, se imprime:
Function f(double) Function h(int)
Parece que GCC está buscando los nombres dependientes del parámetro de la plantilla en la plantilla después de que se haya compilado todo lo demás, por lo que encuentra tanto
h(double)
comoh(int)
, y prefiere este último.Cuando lo compilo usando Clang 2.8, no se compila. El error del compilador es:
ibm_example.cc:8:3: error: use of undeclared identifier ''h'' h(a); ^ ibm_example.cc:16:3: note: in instantiation of function template specialization ''g<int>'' requested here g<int>(234); ^ 1 error generated.
Parece que Clang está buscando los nombres dependientes de parámetros de plantilla en la plantilla en el punto donde se declara la plantilla, por lo que no encuentra ni
h(double)
nih(int)
.
¿Cuál es la correcta?