c++ - programacion - Necesidad de funciones de plantilla de declaración directa
template typename t c++ (2)
Recientemente creé este código de ejemplo para ilustrar el uso de la función de la plantilla variadic de C ++ 11.
template <typename Head, typename... Tail> void foo (Head, Tail...);
template <typename... Tail> void foo (int, Tail...);
void foo () {}
template <typename... Tail>
void foo (int x, Tail... tail)
{
std :: cout << "int:" << x;
foo (tail...);
}
template <typename Head, typename... Tail>
void foo (Head x, Tail... tail)
{
std :: cout << " ?:" << x;
foo (tail...);
}
foo (int (123), float (123)); // Prints "int:123 ?:123.0"
Si se omiten las dos primeras líneas que declaran hacia adelante foo
, esto imprime int:123int:123
lugar. Esto sorprendió a cierto programador de C ++ con experiencia y conocimientos.
Estaba convencido de que las declaraciones a futuro no deberían ser necesarias porque el cuerpo no se creará una instancia hasta la segunda fase de la búsqueda de dos fases. Cree que el compilador (gcc 4.6) tiene un error.
Creo que el compilador tiene razón porque las dos foo
son funciones de plantilla base diferentes y la elección de la plantilla base debe estar bloqueada durante la primera fase o, de lo contrario, podría violar la regla de una definición al crear una instancia de foo
antes de que todas las versiones tengan ha sido definido y luego nuevamente (considere cómo el enlazador asume que las definiciones de funciones de plantilla redundantes son idénticas, intercambiables y descartadas).
Entonces, ¿quién tiene razón?
El GOTW anteriormente vinculado explica muy bien cómo y por qué las plantillas de función no se especializan parcialmente, pero la existencia de funciones de plantilla variadic parece aumentar la confusión: la intuición de que foo<int,Tail...>
debería ser una especialización parcial of foo<Head,Tail...>
es más fuerte que la intuición para funciones no variadas, al menos para mí.
GCC (y Clang) tienen razón. MSVC podría equivocarse porque no implementa la búsqueda correctamente.
Parece que hay un malentendido de parte de su colega. Las reglas de búsqueda son:
- la función de plantilla base debe declararse antes de que se llame desde una definición
- La función de plantilla especializada debe declararse antes de ser instanciada
Nota: esas reglas se aplican a funciones libres, dentro de una clase no se requiere una declaración de reenvío
Tenga en cuenta que debido a que una definición también actúa como una declaración, no es necesario, en su ejemplo, reenviar declarar la versión int
.
Ejemplo correcto:
template <typename T> void foo(T); // declare foo<T>
template <typename T> void bar(T t) { foo(t); }// call foo<T> (dependent context)
template <> void foo<int>(int); // declare specialiaztion foo<int>
void bar(int i) { foo(i); } // instantiate foo<T> with int
// which is the specialization
Si hay una plantilla base disponible, esto es un error. Si la especialización no se declara antes de la instanciación, no se utilizará, y esto puede significar, posteriormente, una violación de la regla ODR (si otra instancia utiliza la especialización).
De la norma (C ++ 0x FDIS):
14.6.4.2
1. Para una llamada de función que depende de un parámetro de plantilla, las funciones candidatas se encuentran utilizando las reglas de búsqueda habituales (3.4.1, 3.4.2, 3.4.3), excepto que:
- Para la parte de la búsqueda que utiliza la búsqueda de nombre no calificado (3.4.1) o la búsqueda de nombre calificado (3.4.3), 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.
Si el nombre de la función es un id no calificado y la llamada se formaría de forma incorrecta o se encontraría 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 Esas declaraciones encontradas en los contextos de definición de plantilla y creación de instancias de plantilla, entonces el programa tiene un comportamiento indefinido.
Tenga en cuenta que los párrafos mencionados son para funciones regulares.
La búsqueda de dos fases encontrará:
- funciones que son visibles en el punto de definición, y
- Funciones que se pueden encontrar por ADL en el punto de instanciación.
ADL no puede encontrar la template <typename Head, typename... Tail> void foo (Head x, Tail... tail)
, por lo que si no está visible en el punto de definición, no se encontrará en absoluto .
En otras palabras, GCC tiene razón.