He estado jugando con clang por un tiempo, y me encontré con "test / SemaTemplate /pendent-template-recovery.cpp" (en la distribución de clang) que se supone que proporciona pistas para recuperarse de un error de plantilla.
Todo se puede reducir fácilmente a un ejemplo mínimo:
template<typename T, typename U, int N> struct X {
void f(T* t)
{
// expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}}
t->f0<U>();
}
};
El mensaje de error producido por clang:
tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name
t->f0<U>();
^
template
1 error generated.
... Pero me cuesta entender dónde se supone exactamente que se debe insertar la template
palabra clave para que el código sea sintácticamente correcto.
Respuestas:
ISO C ++ 03 14.2 / 4:
En
t->f0<U>();
f0<U>
es una especialización de plantilla de miembro que aparece después->
y que depende explícitamente del parámetro de plantillaU
, por lo que la especialización de plantilla de miembro debe tener el prefijo detemplate
palabra clave.Así que cámbiate
t->f0<U>()
at->template f0<U>()
.fuente
t->(f0<U>())
lo habría solucionado, como pensé que lo pondríaf0<U>()
en una expresión independiente ... bueno, pensé mal, parece ...Además de los puntos que hicieron otros, observe que a veces el compilador no puede tomar una decisión y ambas interpretaciones pueden producir programas válidos alternativos al crear instancias
#include <iostream> template<typename T> struct A { typedef int R(); template<typename U> static U *f(int) { return 0; } static int f() { return 0; } }; template<typename T> bool g() { A<T> a; return !(typename A<T>::R*)a.f<int()>(0); } int main() { std::cout << g<void>() << std::endl; }
Se imprime
0
al omitirtemplate
antesf<int()>
pero1
al insertarlo. Lo dejo como ejercicio para averiguar qué hace el código.fuente
f<U>
y siempre imprime1
, lo que para mí tiene mucho sentido. Todavía no entiendo por quétemplate
se requiere la palabra clave y qué diferencia hace.template
se necesita: stackoverflow.com/questions/610245/… sin depender únicamente de términos estándar que son difíciles de entender. Informe si algo en esa respuesta sigue siendo confuso.Insértelo justo antes del punto donde está el símbolo de intercalación:
template<typename T, typename U, int N> struct X { void f(T* t) { t->template f0<U>(); } };
Editar: la razón de esta regla se vuelve más clara si piensa como un compilador.
Los compiladores generalmente solo miran hacia adelante uno o dos tokens a la vez, y generalmente no "miran hacia adelante" al resto de la expresión.[Editar: ver comentario] El motivo de la palabra clave es el mismo que el que necesitatypename
para indicar nombres de tipos dependientes: le dice al compilador "oye, el identificador que estás a punto de ver es el nombre de una plantilla, en lugar de el nombre de un miembro de datos estáticos seguido de un signo menor que ".fuente
template
. Hay casos en los que tanto con como sintemplate
producirán programas válidos con comportamiento diferente. Así que esto no es solo un problema sintáctico (t->f0<int()>(0)
es válido sintácticamente tanto para la versión de lista de argumentos de plantilla como para la versión menor que).Extracto de Plantillas C ++
La construcción .template Se descubrió un problema muy similar después de la introducción de typename. Considere el siguiente ejemplo utilizando el tipo de conjunto de bits estándar:
template<int N> void printBitset (std::bitset<N> const& bs) { std::cout << bs.template to_string<char,char_traits<char>, allocator<char> >(); }
La construcción extraña en este ejemplo es .template. Sin ese uso adicional de la plantilla, el compilador no sabe que el token menor que (<) que sigue no es realmente "menor que" sino el comienzo de una lista de argumentos de plantilla. Tenga en cuenta que esto es un problema solo si la construcción antes del período depende de un parámetro de plantilla. En nuestro ejemplo, el parámetro bs depende del parámetro de plantilla N.
En conclusión, la notación .template (y notaciones similares como -> template) debe usarse solo dentro de las plantillas y solo si siguen algo que depende de un parámetro de plantilla.
fuente