c++ - que - template typename
Oficialmente, ¿para qué sirve typename? (7)
Considera el código
template<class T> somefunction( T * arg )
{
T::sometype x; // broken
.
.
Desafortunadamente, no se requiere que el compilador sea psíquico, y no sabe si T :: tipo terminará refiriéndose a un nombre de tipo o un miembro estático de T. Entonces, uno usa typename
para contarlo:
template<class T> somefunction( T * arg )
{
typename T::sometype x; // works!
.
.
En ocasiones, he visto algunos mensajes de error realmente indescifrables escupidos por gcc al usar plantillas ... Específicamente, he tenido problemas donde las declaraciones aparentemente correctas estaban causando errores de compilación muy extraños que mágicamente desaparecieron al agregar como prefijo la palabra clave "typename" a el comienzo de la declaración ... (Por ejemplo, la semana pasada, declaraba dos iteradores como miembros de otra clase con plantillas y tenía que hacer esto) ...
¿Cuál es la historia sobre typename?
Dos usos:
- Como una palabra clave de argumento de plantilla (en lugar de ''clase'')
- Una palabra clave typename le dice al compilador que un identificador es un tipo (en lugar de una variable miembro estática)
template <typename T> class X // [1] { typename T::Y _member; // [2] }
El secreto reside en el hecho de que una plantilla puede ser especializada para algunos tipos. Esto significa que también puede definir la interfaz completamente diferente para varios tipos. Por ejemplo, puedes escribir:
template<typename T>
struct test {
typedef T* ptr;
};
template<> // complete specialization
struct test<int> { // for the case T is int
T* ptr;
};
Uno podría preguntarse por qué es útil y de hecho: Eso realmente parece inútil. Pero tenga en cuenta que, por ejemplo, std::vector<bool>
el tipo de reference
ve completamente diferente que para otras T
s. Es cierto que no cambia el tipo de reference
de un tipo a algo diferente, pero sin embargo podría suceder.
Ahora qué sucede si escribe sus propias plantillas usando esta plantilla de test
. Algo como esto
template<typename T>
void print(T& x) {
test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
parece estar bien para ti porque esperas que la test<T>::ptr
sea un tipo. Pero el compilador no sabe y, de hecho, incluso el estándar le aconseja que espere lo contrario, test<T>::ptr
no es un tipo. Para decirle al compilador lo que espera, debe agregar un typename
antes. La plantilla correcta se ve así
template<typename T>
void print(T& x) {
typename test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
En typename
: debe agregar typename
antes siempre que use un tipo anidado de una plantilla en sus plantillas. (Por supuesto, solo si se usa un parámetro de plantilla de su plantilla para esa plantilla interna).
En algunas situaciones en las que se refiere a un miembro del denominado tipo dependiente (que significa "dependiente del parámetro de plantilla"), el compilador no siempre puede deducir inequívocamente el significado semántico de la construcción resultante, porque no sabe qué tipo de nombre es (es decir, si es un nombre de un tipo, un nombre de un miembro de datos o el nombre de otra cosa). En casos como ese, debe eliminar la ambigüedad de la situación al decirle explícitamente al compilador que el nombre pertenece a un nombre de tipo definido como miembro de ese tipo dependiente.
Por ejemplo
template <class T> struct S {
typename T::type i;
};
En este ejemplo, la palabra clave typename
necesaria para que el código se compile.
Lo mismo sucede cuando desea hacer referencia a un miembro de plantilla de tipo dependiente, es decir, a un nombre que designa una plantilla. También debe ayudar al compilador utilizando la template
palabra clave, aunque se coloca de forma diferente
template <class T> struct S {
T::template ptr<int> p;
};
En algunos casos, podría ser necesario usar ambos
template <class T> struct S {
typename T::template ptr<int>::type i;
};
(si obtuve la sintaxis correctamente).
Por supuesto, otra función de la palabra clave typename
se usará en las declaraciones de parámetros de la plantilla.
La siguiente es la cita del libro de Josuttis:
La palabra clave typename se introdujo para especificar que el identificador que sigue es un tipo. Considere el siguiente ejemplo:
template <class T> Class MyClass { typename T::SubType * ptr; ... };
Aquí, typename se usa para aclarar que SubType es un tipo de clase T. Por lo tanto, ptr es un puntero al tipo T :: SubType. Sin typename, SubType se consideraría un miembro estático. Así
T::SubType * ptr
sería una multiplicación de valor Subtipo de tipo T con ptr.
La publicación BLog de Stan Lippman sugiere:
Stroustrup reutilizó la palabra clave de clase existente para especificar un parámetro de tipo en lugar de introducir una nueva palabra clave que podría romper los programas existentes. No se trató de considerar una nueva palabra clave, sino que no se consideró necesaria dada su posible interrupción. Y hasta el estándar ISO-C ++, esta era la única forma de declarar un parámetro de tipo.
Así que, básicamente, Stroustrup reutilizó la palabra clave class sin introducir una nueva palabra clave que luego se modifique en la norma por los siguientes motivos
Como el ejemplo dado
template <class T>
class Demonstration {
public:
void method() {
T::A *aObj; // oops …
// …
};
la gramática del lenguaje malinterpreta T::A *aObj;
como una expresión aritmética por lo que se introduce una nueva palabra clave llamada typename
typename T::A* a6;
instruye al compilador a tratar el enunciado siguiente como una declaración.
Dado que la palabra clave estaba en la nómina, diablos, ¿por qué no solucionar la confusión causada por la decisión original de reutilizar la palabra clave class?
Por eso tenemos ambos
Puedes echarle un vistazo a esta publicación , definitivamente te será útil, simplemente extraje todo lo que pude
#include <iostream>
class A {
public:
typedef int my_t;
};
template <class T>
class B {
public:
// T::my_t *ptr; // It will produce compilation error
typename T::my_t *ptr; // It will output 5
};
int main() {
B<A> b;
int my_int = 5;
b.ptr = &my_int;
std::cout << *b.ptr;
std::cin.ignore();
return 0;
}