Pregunta Oficialmente, ¿para qué sirve typename?


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?


88
2017-10-21 13:45


origen


Respuestas:


La siguiente es la cita del libro de Josuttis:

La palabra clave typename se introdujo en   especifique que el identificador que   sigue es un tipo. Considera el   siguiente ejemplo:

template <class T>
Class MyClass
{
  typename T::SubType * ptr;
  ...
};

Aquí, typename se usa para aclarar   SubType es un tipo de clase T. Por lo tanto,   ptr es un puntero al tipo   T :: SubType. Sin nombre de tipo, Subtipo   sería considerado un miembro estático.   Así

T::SubType * ptr

sería una multiplicación de valor   Subtipo de tipo T con ptr.


142
2017-10-21 13:51



Publicación BLog de Stan Lippman sugiere:

Stroustrup reutilizó la clase existente   palabra clave para especificar un parámetro de tipo   en lugar de introducir una nueva palabra clave   que, por supuesto, podría romper las existentes   programas. No fue una nueva palabra clave   no fue considerado, solo que   no se consideró necesario dado su   interrupción potencial. Y hasta el   Estándar ISO-C ++, este fue el único   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.

Como la palabra clave estaba en la nómina,   Diablos, ¿por qué? no arreglar la confusión causada   por la decisión original reutilizar el   palabra clave de clase

Por eso tenemos ambos

Puedes echar un vistazo a esta publicación, definitivamente lo ayudará, simplemente extraje todo lo que pude


23
2017-10-21 13:50



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 decirlo:

template<class T> somefunction( T * arg )
{
    typename T::sometype x; // works!
    .
    .

12
2017-10-21 13:53



En algunas situaciones en las que se refiere a un miembro de los llamados dependiente tipo (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 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 typenameen necesario para el código para compilar.

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 palabra clave template, aunque se coloca de manera 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, otro rol de la palabra clave typename se debe usar en las declaraciones de parámetros de la plantilla.


5
2017-10-21 14:39



Dos usos:

  1. Como una palabra clave de argumento de plantilla (en lugar de 'clase')
  2. 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] 
}

4
2017-10-21 13:54



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 reference tipo se ve completamente diferente que para otra Ts. 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 esto test modelo. 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 esperar ese test<T>::ptr es 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 de. 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 pocas palabras: debe agregar typename antes cada vez que utiliza 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).


3
2017-10-21 14:36



#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;
}

1
2018-06-27 13:22