template - typename c++
Uso de la palabra clave typename con los parámetros de la función de plantilla (3)
En primer lugar, no creo que haya habido una intención de hacer una distinción nítida y precisa entre las situaciones en las que solo se permiten nombres tipográficos (como el nombre de la clase base) y situaciones en las que también se permiten entidades sin tipo (expresiones similares). Diría que el contexto del nombre de la clase base fue seleccionado por alguna otra razón.
En segundo lugar, no es exactamente correcto decir que en las declaraciones de parámetros de funciones cada entidad es necesariamente un nombre de tipo. Puede declarar un parámetro de la siguiente manera
template <class T>
void foo(const T::type& v[T::value]);
Por supuesto, la gramática en este caso explícitamente dicta que ese type
debe ser un nombre de tipo y el value
debe ser un valor. Sin embargo, el compilador solo puede darse cuenta después del análisis sintáctico de la declaración, mientras que creo que la idea de typename
fue introducida para ayudar al compilador a comenzar realmente el análisis sintáctico apropiado del código, es decir, la distinción debería estar disponible antes del sintáctico análisis, como una entrada en el análisis sintáctico. Esta distinción podría tener profundos efectos en la interpretación del código.
En C ++, se necesita la palabra clave typename
para que el compilador pueda typename
la typename
entre los tipos anidados y los valores anidados en las plantillas. Sin embargo, hay ciertas situaciones en las que no es posible la ambigüedad, como cuando una clase derivada hereda de un tipo de clase anidada.
template <class T>
class Derived : public T::type
{ };
Aquí la palabra clave typename
no es obligatoria, y de hecho ni siquiera está permitida. Esto tiene sentido, porque el contexto elimina la ambigüedad. Aquí, T::type
debe referirse a un tipo, ya que obviamente no se puede heredar de un valor.
Creo que lo mismo sería válido para los parámetros de la plantilla de función.
template <class T>
void foo(const T::type& v)
{
}
En este caso, el contexto deja en claro que T::type
debe hacer referencia a un tipo, ya que un parámetro de función no puede ser un valor. Sin embargo, el compilador no acepta esto. Quiere const typename T::type&
. Esto parece inconsistente ¿Por qué el lenguaje permite la suposición implícita de un tipo anidado en el contexto de la herencia, pero no en el contexto de los parámetros de la función? En ambos casos no puede haber ambigüedad, entonces ¿por qué la necesidad de typename
en uno pero no en el otro?
Sería interesante encontrar lo que está causando esto.
He intentado leer el estándar en busca de una respuesta, tenga en cuenta que soy un novato en esto.
Sin embargo, creo que he encontrado una cláusula relevante.
§14.6.2. Se supone que un nombre utilizado en una declaración o definición de plantilla y que depende de un parámetro de plantilla no nombra un tipo a menos que la búsqueda de nombre aplicable encuentre un nombre de tipo o el nombre esté calificado por la palabra clave typename .
Supongo que esto implica que el problema radica en la diferencia de cómo funciona la búsqueda de nombres para las listas de especificadores básicos y los argumentos de las funciones.
Búsqueda del nombre del especificador base:
§10.2. Durante la búsqueda de un nombre de clase base, se ignoran los nombres que no son de tipo (3.3.10).
Lo que explica por qué typename no es necesario para los especificadores básicos.
Todavía estoy buscando búsqueda de nombre de argumento de función.
Por favor corrígeme si esta es una suposición incorrecta o irrelevante. Mientras tanto, mientras sigo cavando.
El error dado por VS2010 cuando no califica el argumento de la plantilla en la declaración de la función es el siguiente:
''T :: tipo'': el nombre dependiente no es un prefijo de tipo con ''nombre de tipo'' para indicar un tipo.
Sin embargo, aún no estoy seguro acerca de las reglas para la búsqueda de nombre de argumento de función dependiente ...
Si cambias ligeramente tu declaración, obtienes una historia completamente diferente
template <class T>
void foo(T::type& v);
Eso ya no es inequívoco. Podría declarar una variable de tipo void
que se inicializa mediante una expresión AND
bits. La declaración completa sería una plantilla. Por supuesto, esto semánticamente es una tontería, pero sintácticamente está bien.
La apariencia de un solo const
sintácticamente lo hace inequívoco, pero es demasiada dependencia del contexto para hacer que esto funcione en un compilador. Tiene que recordar que lee un const
o cualquier otra cosa, y cuando analiza el T::type
después tendrá que recordar tomar este nombre como un tipo. También inflaría aún más al Estándar ya complicado más allá de toda creencia.
Cambiemos nuevamente su declaración de función
template <class T>
void foo(const T::type);
Ni siquiera la aparición de const
allí proporciona un análisis inequívoco. ¿Debería ser una declaración de función con un parámetro sin nombre, o debería ser una declaración de función con un nombre de parámetro inválido que omita su tipo? El nombre de un parámetro es analizado por un declarator-id
, que también puede ser un nombre calificado. Entonces, aquí, el const
pertenecerá a los especificadores de tipo, mientras que el compilador analizará el T::type
como el nombre del parámetro, en ausencia de un typename
. Eso es totalmente absurdo, pero es sintácticamente válido .
En el caso de nombres de clase base, la búsqueda de nombre en sí misma establece que los nombres que no son de tipo se ignoran. Por lo tanto, obtienes la omisión de typename
de forma gratuita: el nombre que la búsqueda de nombres cede a los módulos de mayor nivel del compilador se refiere a un tipo o la búsqueda de nombres dará un error.
He escrito una entrada de preguntas frecuentes sobre dónde colocar la "plantilla" y "nombre de tipo" en los nombres dependientes .