c++ - template - ¿Por qué se necesita la palabra clave "typename" antes de los nombres dependientes calificados y no antes de los nombres independientes calificados?
template vector c++ (3)
class A
{
static int iterator;
class iterator
{
[...]
};
[...]
};
Yo (creo que) entiendo la razón por la typename
se necesita typename
aquí:
template <class T>
void foo() {
typename T::iterator* iter;
[...]
}
pero no entiendo la razón por la que no se necesita typename
aquí:
void foo() {
A::iterator* iter;
[...]
}
¿Alguien puede explicar?
EDITAR:
La razón por la que el compilador no tiene un problema con este último, encontré una buena respuesta en un comentario:
en el caso de A::iterator
no veo por qué el compilador no lo confundiría con el static int iterator
? - xcrypt
@xcrypt porque sabe lo que son ambos A::iterator
y puede elegir cuál dependiendo de cómo se use - Seth Carnegie
La razón por la que el compilador necesita un typename
antes de los nombres dependientes calificados, en mi opinión se responde muy bien en la respuesta aceptada por Kerrek SB. Asegúrese de leer también los comentarios sobre esa respuesta, especialmente este de iammilind:
"T :: A * x ;, esta expresión puede ser verdadera para ambos casos donde T :: A es un tipo y T :: A es un valor. Si A es un tipo, entonces resultará en una declaración de puntero; si A es un valor, entonces resultará en una multiplicación. Por lo tanto, una sola plantilla tendrá un significado diferente para 2 tipos diferentes, lo que no es aceptable ".
Como el compilador no sabe qué es T::iterator
hasta que se crea una instancia de la plantilla, no sabe si iterator
es una variable de clase, un tipo, una función o qué. Necesitas decirle que es un tipo usando typename
.
Porque en el foo no contemplado, está realizando una operación de multiplicación válida (suponiendo que haya declarado iter
antes de ese uso). Intenta omitir la estrella y obtendrás un error de compilación. El int esconde la clase.
La palabra clave typename no impide que se oculte. Solo gcc lo implementa incorrectamente para hacerlo. Entonces, si intenta crear una instancia de su plantilla de función con A
como tipo, obtendrá un error de compilación, porque el nombre especificado después de typenake se referirá a un no tipo en cualquier compilador compatible estándar.
Un nombre en C ++ puede pertenecer a tres niveles diferentes de entidades: valores, tipos y plantillas.
struct Foo
{
typedef int A;
static double B;
template <typename T> struct C;
};
Los tres nombres Foo::A
, Foo::B
y Foo::C
son ejemplos de los tres niveles diferentes.
En el ejemplo anterior, Foo
es un tipo completo, por lo que el compilador ya sabe a qué se refieren Foo::A
etc. Pero ahora imagina esto:
template <typename T> struct Bar
{
T::A x;
};
Ahora estamos en problemas: ¿qué es T::A
? si T = Foo
, entonces T::A = int
, que es un tipo, y todo está bien. Pero cuando T = struct { char A; };
T = struct { char A; };
, entonces T::A
es un valor , que no tiene sentido.
Por lo tanto, el compilador exige que le digas qué se supone que son T::A
y T::B
y T::C
Si no dices nada, se asume que es un valor. Si dice " typename
, es un "typename", y si dice " template
, es una plantilla:
template <typename T> struct Bar
{
typename T::A x; // ah, good, decreed typename
void foo()
{
int a = T::B; // assumed value, OK
T::template C<int> z; // decreed template
z.gobble(a * x);
}
};
Las comprobaciones secundarias, como si T::B
es convertible a int
, si a
y x
se pueden multiplicar, y si C<int>
realmente tiene una función miembro, se aplazan todos hasta que realmente se crea una instancia de la plantilla. Pero la especificación de si un nombre denota un valor, un tipo o una plantilla es fundamental para la corrección sintáctica del código y debe proporcionarse allí durante la definición de la plantilla.