2010-12-03 17 views
28

En C++, se necesita la palabra clave typename para que el compilador pueda desambiguar 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.Uso de la palabra clave typename con los parámetros de función de plantilla

template <class T> 
class Derived : public T::type 
{ }; 

Aquí no se requiere la palabra clave typename, y es, de hecho, ni siquiera permitido. Esto tiene sentido, porque el contexto elimina la ambigüedad. Aquí, T::type debe hacer referencia a un tipo, ya que obviamente no puede heredar de un valor.

Creo que lo mismo sucedería con los parámetros de la plantilla de función.

template <class T> 
void foo(const T::type& v) 
{ 

} 

En este caso, el contexto deja claro que T::type debe hacer referencia a un tipo, ya que un parámetro de la 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?

+3

Esta es una pregunta muy interesante. –

+1

Los miembros del comité de C++ son humanos. Envíe un cambio propuesto para el siguiente estándar. –

+0

@ Channel72 durante mi breve contacto con las plantillas Me he dado cuenta de que hay muchas más reglas inconsistentes y no ortogonales. No puedo decir que las plantillas estuvieran mal diseñadas, pero definitivamente hay espacio (habitación de invitados, no una pequeña sala de cajas) para mejorar. –

Respuesta

22

Si cambia ligeramente su declaración, se obtiene toda una historia 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 de AND en 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 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.

Vamos de nuevo cambiar su declaración de la función

template <class T> 
void foo(const T::type); 

Ni siquiera la aparición de const allí establece un análisis sintáctico inequívoca. ¿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 declarator-id, que también puede ser un nombre calificado. Así que aquí, el const pertenecerá a los especificadores de tipo, mientras que el T::type será analizado por el compilador como el nombre del parámetro, en ausencia de un typename. Eso tampoco tiene sentido, but is syntactically valid.

En el caso de los nombres de la clase base buscarse a sí mismo nombre indica que los nombres sin tipo se ignoran. Por lo tanto, obtiene la omisión de typename de forma gratuita: el nombre que la búsqueda del nombre cede a los módulos de mayor nivel del compilador se refiere a un tipo, o la búsqueda de nombres habrá dado un error.

He escrito una entrada de preguntas frecuentes sobre Where to put the "template" and "typename" on dependent names.

+3

No sé si debería sorprenderme por descubrir esos casos, o realmente temer por tu cordura :) –

+0

¡Desearía haber recibido más de 1 voto favorable, respuesta increíble! – Praetorian

2

Sería interesante encontrar lo que está causando esto.

He estado tratando de 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. Un nombre utilizado en una plantilla declaración o definición y que es depende de un parámetro de plantilla es supone no nombrar a un tipo a menos que el búsqueda de nombre aplicables encuentra un nombre de tipo o el nombre es calificado por la palabra clave nombretipo.

Supongo que esto implica que el problema radica en la diferencia de cómo funciona la búsqueda de nombres para las listas de especificador base y los argumentos de función.

Base nombre especificador de búsqueda:

§10.2. Durante la búsqueda de una base nombre de clase, se ignoran los nombres que no son de tipo (3.3.10).

que explica por qué nombretipo no es necesaria para los especificadores de base.

Sigue buscando la búsqueda del nombre del argumento de función.

Corrígeme si se trata de una suposición incorrecta o irrelevante. Mientras tanto, mientras sigo cavando.

El error dado por VS2010 cuando no calificar el argumento de plantilla en la declaración de la función es la siguiente:

'T :: tipo': el nombre del dependiente no es un prefijo tipo con 'nombre de tipo' a indicar un tipo.

Sin embargo, todavía estoy claro sobre las reglas para la función dependiente del nombre de argumento de búsqueda ...

+2

A Requisito estándar para no perderse esta discusión: C++ 03 14.6p5: "La palabra clave' typename' no está permitida en * base-specifier * o en * mem-initializer *; en estos contextos a * qualified- id * que depende de * template-parameter * (14.6.2 temp.dep) se supone implícitamente que es un nombre de tipo. " – aschepler

4

En primer lugar, no creo que haya habido una intención de hacer una distinción precisa entre las situaciones en las que solo se permiten nombres de tipo (como el nombre de la clase base) y situaciones en las que también se permiten entidades sin tipo (como expresiones). 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. Se 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 dicta explícitamente que type debe haber un nombre de tipo y value debe ser un valor.Sin embargo, el compilador solo puede averiguar eso después de el análisis sintáctico de la declaración, mientras que creo que la idea de typename fue introducida para ayudar al compilador en realidad comenzando el análisis sintáctico apropiado del código, es decir, la distinción debe ser disponible antes de el análisis sintáctico, como una entrada en el análisis sintáctico. Esta distinción podría tener profundos efectos en la interpretación del código.

Cuestiones relacionadas