¿Cómo exactamente una declaración de plantilla coincide con una definición de plantilla? Encontré un texto en el estándar sobre plantilla-ids que hace referencia a la misma función si "sus nombres de plantilla [...] se refieren a la misma plantilla y [...]" (14.4 [temp.temporal] p1) pero no puedo encontrar una definición para los nombres de plantilla o cuando nombres de plantilla se refieren a la misma plantilla. No estoy seguro si estoy en el camino correcto porque no he descifrado la gramática lo suficiente como para decir si un plantilla-id es parte de la definición/declaración de una plantilla, o simplemente el uso de una modelo.¿Cómo se relacionan las definiciones de plantilla con las declaraciones de plantilla?
Por ejemplo, el siguiente programa funciona bien.
#include <iostream>
template<typename T>
T foo(T t);
int main() {
foo(1);
}
template<typename T>
T foo(T t)
{ std::cout << "A\n"; return 0; }
Si cambio la forma en que uso los parámetros de plantilla en la definición de plantilla los nombres al parecer ya no se refieren a la misma plantilla, y la falla de enlace.
#include <iostream>
template<typename T>
T foo(T t);
int main() {
foo(1);
}
template<typename T>
int foo(T t) { std::cout << "A\n"; return 0; }
// or
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
typename identity<T>::type
foo(T t) { std::cout << "A\n"; return 0; }
A continuación, si muevo la definición de plantilla a otra unidad de traducción, para mi aplicación de C++ (MSVC 11 beta) El programa funciona sin importar lo que digo los tipos.
//main.cpp
template<typename T>
T foo(T t);
int main() {
foo(1);
}
//definition.cpp
#include <iostream>
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
typename identity<T>::type
foo(T t) { std::cout << "A\n"; return 0; }
template int foo<int>(int);
o
//definition.cpp
#include <iostream>
template<typename T>
int foo(T t) { std::cout << "A\n"; return 0; }
template int foo<int>(int);
o incluso si la definición no es una plantilla en absoluto:
//definition.cpp
#include <iostream>
int foo(T t) { std::cout << "A\n"; return 0; }
Obviamente vinculación está teniendo éxito porque la firma/nombre revuelto es el mismo, independientemente de la plantilla que fue instanciada para crear el símbolo. Creo que este comportamiento no definido porque yo estoy violando:
§ 14.1 [temp] P6
Una plantilla de función, función miembro de una plantilla de clase, o miembro de datos estática de una plantilla de clase deberá se definirá en cada unidad de traducción en la que esté instanciado implícitamente (14.7.1) a menos que la especialización correspondiente esté explícitamente instanciada (14.7.2) en alguna unidad de traducción; no se requiere diagnóstico.
Pero luego dicen que trato de cumplir con esos requisitos, poniendo una definición de la plantilla en la segunda unidad de traducción, y que incluye una instanciación explícita en uno de dos lugares:
#include <iostream>
template<typename T>
T foo(T t) { std::cout << "A\n"; return 0; }
// Location 1
template<typename T>
int foo(int t) { std::cout << "B\n"; return 0; }
// Location 2
¿Cuáles son las reglas sobre la desambiguación de a qué plantilla se refiere una instanciación explícita? Poniéndolo en la Ubicación 1 hace que se cree una instancia de la plantilla correcta y esa definición se use en el programa final, mientras que ponerlo en la Ubicación 2 ejemplifica la otra plantilla y causa lo que creo que es un comportamiento indefinido bajo 14.1 p6 arriba.
Por otro lado una instancia implícita de dos definiciones plantillas recoge la primera plantilla no importa qué, así que parece que la regla para eliminar la ambigüedad de las plantillas es diferente en estas circunstancias:
#include <iostream>
template<typename T>
T foo(T t) { std::cout << "A\n"; return 0; }
template<typename T>
int foo(int t) { std::cout << "B\n"; return 0; }
int main() {
foo(1); // prints "A"
}
La razón de este vino arriba está relacionada con this question donde el interrogador descubrió que una sola declaración adelantada
template<typename T>
T CastScriptVarConst(const ScriptVar_t& s);
no podría actuar como una declaración de múltiples definiciones de plantilla:
template<typename T>
typename std::enable_if<GetType<T>::value < SVT_BASEOBJ,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
return (T) s;
}
template<typename T>
typename std::enable_if<!(GetType<T>::value < SVT_BASEOBJ)
&& std::is_base_of<CustomVar,T>::value,T>::type
CastScriptVarConst(const ScriptVar_t& s) {
return *s.as<T>();
}
Y quería entender mejor la relación entre las definiciones de las plantillas y las declaraciones.
Busque una copia de "C++ Templates: The Complete Guide" para la historia en toda regla. Y sí, es más digerible que el estándar ... mucho en realidad;) – 0xC0000022L