Tengo el siguiente código:
template <typename TC>
class C
{
struct S
{
template <typename TS>
void fun() const
{}
};
void f(const S& s)
{
s.fun<int>();
}
};
// Dummy main function
int main()
{
return 0;
}
Al compilar esto con gcc 9.2 y clang (9.0), obtengo un error de compilación debido a que template
se requiere la palabra clave para invocar fun
. Clang muestra:
error: use 'template' keyword to treat 'fun' as a dependent template name
s.fun<int>();
^
template
No entiendo por qué el compilador piensa que fun
es un nombre dependiente en el contexto de f
, ya f
que no es una plantilla en sí. Si cambio C
a ser una clase regular en lugar de una plantilla, el error desaparece; Sin embargo, no veo por qué debería haber un error en primer lugar, ya que S
ni f
dependen ni dependen de él TC
.
Por extraño que parezca, MSVC 19.22 compila esto muy bien.
Nota
Antes de votar para cerrar como engañado de ¿Dónde y por qué tengo que poner las palabras clave "plantilla" y "nombre de tipo"? tenga en cuenta que este es un caso especial en el que incluso si de S
hecho es un nombre dependiente, en el contexto de f
este no sería dependiente si no fuera por el hecho de que son miembros de la instanciación actual.
Respuestas:
Considera :
s.a<0>(i)
se analiza como una expresión que contiene dos operaciones de comparación<
y>
, y esto está muy bien para # 1, pero falla por # 2.Si esto se cambia a
s.template a<0>(i)
# 2, entonces está bien y # 1 falla. Por lo tanto, latemplate
palabra clave nunca es redundante aquí.MSVC es capaz de interpretar la expresión en
s.a<0>(i)
ambos sentidos dentro del mismo programa. Pero esto no es correcto según la Norma; cada expresión debe tener solo un análisis para que el compilador lo maneje.fuente
C
u otra, pero nunca puede crear una instancia de ambas. Latemplate
palabra clave aquí sigue siendo innecesaria en mi humilde opinión, porque lo queS
se elige depende de quéC
instancia. Sin latemplate
palabra clave, podría crear una instancia de ambos, y el comportamiento de f sería diferente para cada instancia.<
sea un operador de comparación en una instanciación de plantilla y un paréntesis angular de apertura en otra instanciación. Esto es para garantizar que los compiladores puedan analizar las plantillas en un AST (con marcadores de posición para los tipos de plantillas).fun
puede o no ser una función de plantilla (o puede no existir en absoluto) dependiendo del parámetro de plantilla declass C
.Eso es porque puedes especializarte
S
(sin especializarteC
):Debido a que el compilador quiere saber si
fun
es una plantilla o no cuando la mira por primera vezclass C
(antes de sustituir el parámetro de la plantilla),template
es obligatorio.fuente
S
alguna vez se puede acceder a estas nuevas definiciones def
. Si no pueden, tener esta restricción no tiene sentido porquef
no podrá verlos de todos modos.f
lo encuentra.