c++ - Uso de la palabra clave typename con typedef y new
templates language-lawyer (3)
Entonces, ¿por qué los compiladores todavía necesitan la palabra clave typename?
Debido a que las reglas del lenguaje se formulan de esa manera: cada nombre dependiente que se usa en un contexto de tipo debe ir precedido por typename
( mutatis mutandis , lo mismo se aplica al nombre de la template
y template
palabra clave de la template
). Ahora, ¿por qué las reglas del lenguaje no hacen la diferencia entre los casos donde se necesita el nombre de tipo para eliminar la ambigüedad y aquellos donde el contexto proporciona suficiente información? Probablemente (no estaba allí cuando se tomó la decisión) de mantener la descripción del lenguaje para que sea aún más compleja (considere las consecuencias de los casos faltantes, de una manera u otra).
En su ejemplo, X no es un nombre de tipo (esa posibilidad si es por compatibilidad con C donde el nombre de la etiqueta no es automáticamente el nombre del tipo), por lo que necesita yuse struct
:
pX = new struct T::X;
Considere este código,
template<class T>
struct Sample
{
typename T::X *x; //declare pointer to T''s X
};
En el código anterior, el typename
requiere la palabra clave typename
, de modo que pueda desambiguar los tipos anidados y los valores anidados en las plantillas. Eso significa que, en ausencia de la palabra clave typename
, el compilador interpretaría esto como una multiplicación de T :: X con x,
T::X *x; //multiply T::X with x
Por lo tanto, en situaciones en las que puede surgir ambigüedad, la palabra clave typename
convierte en una necesidad para eliminar las ambigüedades. Pero hay pocas situaciones en las que el contexto mismo elimina las ambigüedades. El otro tema discute los contextos de la clase base y los parámetros de función (aunque este último no elimina la ambigüedad). En este tema, en particular quiero discutir otros dos contextos que parecen no ser ambiguos , pero aún estamos obligados a escribir el typename
,
typedef typename T::X xtype;
pX = new typename T::X;
En estas dos situaciones, las palabras clave typedef
y new
hacen lo suficientemente claro para el compilador que lo que sigue es tipo , no valor .
Entonces, mi pregunta es, ¿por qué los compiladores todavía necesitan la palabra clave typename
, incluso en situaciones no ambiguas como cuando usamos typedef
y new
?
EDITAR (después de leer esta respuesta de Johannes Schaub ):
//typedef NOT followed by a type!
int typedef A;
Esta sintaxis me pide que modifique un poco mi pregunta, para que otros puedan ver el punto que estoy tratando de señalar.
Considera esto,
T::X typedef *x;
Entonces, desde el contexto, aún es lo suficientemente claro para el compilador que T :: X es un tipo, sin importar si aparece antes de typedef
o después de typedef
. A menos que C ++ nos permita escribir typedef 5 five
o typedef T::value t_value
(donde T :: value es value ), la presencia de typedef
elimina todas las ambigüedades y, por lo tanto, typename
parece ser un requisito innecesario de la Norma (en tal situaciones) . El mismo argumento es válido para los new
también.
Además, he escrito una plantilla de clase que utiliza esta estructura como argumento de plantilla:
struct A
{
struct X { string name; };
static const int X = 100;
};
Particularmente quiero saber si el siguiente código (del constructor) es correcto (portátil) o no,
//two interesting statements
pX = new typename T::X; //T::X means struct X
product = T::X * p; //but here, T::X means int X
El código completo está here en ideone. Por favor, eche un vistazo antes de responder. :-)
La sintaxis de C ++ es más loca que eso.
// typedef NOT followed by a type!
int typedef A;
// new NOT followed by a type!
new (0) int;
Otros han comentado acerca de tu ejemplo. El especificador de nombre de tipo no cede a la búsqueda ignorando los nombres que no son de tipo. Por lo tanto, si dice un new typename T::X
, y hay un nombre de objeto X
en T
, aún se encontrará en lugar del nombre de tipo X
(GCC, sin embargo, ignora los nombres que no son de tipo al buscar un nombre después de un nombre de typename
. Pero eso no es compatible con las normas).
Respuestas a las ediciones:
Considera esto,
T::X typedef *x;
Entonces, desde el contexto, aún es lo suficientemente claro para el compilador que T :: X es un tipo, sin importar si aparece antes de typedef o después de typedef.
El compilador debe saber cuándo se deben especificar los especificadores de declaración y (es decir, la "sección de tipo" y cuándo comienza la sección de declarador (es decir, la sección de "nombres"). Hay declaraciones donde la sección de tipo está vacía:
// constructor definitions don''t need a type section
MyClass::MyClass() { }
// conversion function definitions don''t need a type section
MyClass::operator int() { }
Si el primer nombre que especifica no es un tipo, la sección de tipo finaliza y la sección de nombre comienza. Decir T::X
le dice al compilador:
Ahora quiero definir
T::X
Se lee de izquierda a derecha, por lo que pensará que olvidó un punto y coma cuando se encuentra con el typedef
. Dentro de las clases, la interpretación es ligeramente diferente pero muy parecida a esto también. Eso es un análisis simple y efectivo.
El mismo argumento es válido para los nuevos también.
Tiendo a estar de acuerdo contigo aquí. Sintácticamente, debe ser inequívoco si deja de lado paréntesis. Sin embargo, como nunca he escrito un analizador de C ++, puede que haya errores ocultos que no estoy viendo.
Cada adición de typename
en los casos de esquina del lenguaje como en el new
potencialmente requerirá una cantidad sustancial de diseño tanto para los compiladores como para los redactores de estándares, mientras que aún requiere typename
para la gran mayoría de los otros casos donde se necesita. No creo que esto valga la pena.
Tu código parece entrar en un área muy gris.
Este párrafo sobre el nombre oculto.
Un nombre de clase (9.1) o un nombre de enumeración (7.2) puede estar oculto por el nombre de una variable, miembro de datos, función o enumerador declarado en el mismo ámbito. Si una clase o nombre de enumeración y una variable, miembro de datos, función o enumerador se declaran en el mismo ámbito (en cualquier orden) con el mismo nombre, la clase o el nombre de enumeración se ocultan donde sea que la variable, miembro de datos, función o el nombre del enumerador es visible.
Parece indicar que el compilador tiene razón para quejarse de que A::X
no es un nombre de tipo.