¿Dónde y por qué tengo que poner las palabras clave “plantilla” y “nombre de tipo”?

En plantillas, ¿dónde y por qué tengo que poner typename y template en los nombres dependientes? ¿Qué son exactamente los nombres dependientes de todos modos? Tengo el siguiente código:

 template  // Tail will be a UnionNode too. struct UnionNode : public Tail { // ... template struct inUnion { // Q: where to add typename/template here? typedef Tail::inUnion dummy; }; template struct inUnion { }; }; template  // For the last node Tn. struct UnionNode { // ... template struct inUnion { char fail[ -2 + (sizeof(U)%2) ]; // Cannot be instantiated for any U }; template struct inUnion { }; }; 

El problema que tengo está en la línea typedef Tail::inUnion dummy . Estoy bastante seguro de que inUnion es un nombre dependiente, y VC ++ tiene razón al asfixiarlo. También sé que debería poder agregar una template algún lugar para decirle al comstackdor que inUnion es una plantilla-id. Pero, ¿dónde exactamente? ¿Y debería entonces suponer que inUnion es una plantilla de clase, es decir, inUnion nombra un tipo y no una función?

Para analizar un progtwig C ++, el comstackdor necesita saber si ciertos nombres son tipos o no. El siguiente ejemplo demuestra que:

 t * f; 

¿Cómo debe ser analizado? Para muchos lenguajes, un comstackdor no necesita saber el significado de un nombre para analizar y, básicamente, saber qué acción hace una línea de código. Sin embargo, en C ++, lo anterior puede arrojar interpretaciones muy diferentes según lo que t signifique. Si es un tipo, entonces será una statement de un puntero f . Sin embargo, si no es un tipo, será una multiplicación. Entonces el Estándar de C ++ dice en el párrafo (3/7):

Algunos nombres denotan tipos o plantillas. En general, siempre que se encuentre un nombre, es necesario determinar si ese nombre denota una de estas entidades antes de continuar analizando el progtwig que lo contiene. El proceso que determina esto se llama búsqueda de nombre.

¿Cómo sabrá el comstackdor a qué se refiere un nombre t::x , si t refiere a un parámetro de tipo de plantilla? x podría ser un miembro de datos int estático que podría multiplicarse o bien podría ser una clase anidada o typedef que podría ceder a una statement. Si un nombre tiene esta propiedad, que no se puede buscar hasta que se conozcan los argumentos reales de la plantilla, se denomina nombre dependiente (“depende” de los parámetros de la plantilla).

Puede recomendar esperar solo hasta que el usuario crea la instancia de la plantilla:

Esperemos hasta que el usuario crea la instancia de la plantilla y luego descubramos el significado real de t::x * f; .

Esto funcionará y, de hecho, está permitido por el Estándar como posible enfoque de implementación. Estos comstackdores básicamente copian el texto de la plantilla en un búfer interno, y solo cuando se necesita una instanciación, analizan la plantilla y posiblemente detectan errores en la definición. Pero en lugar de molestar a los usuarios de la plantilla (colegas pobres) con errores cometidos por el autor de una plantilla, otras implementaciones eligen revisar las plantillas desde el principio y dar errores en la definición lo antes posible, incluso antes de que se realice una instancia.

Entonces tiene que haber una forma de decirle al comstackdor que ciertos nombres son tipos y que ciertos nombres no lo son.

La palabra clave “typename”

La respuesta es: Decidimos cómo el comstackdor debería analizar esto. Si t::x es un nombre dependiente, entonces debemos prefijarlo por typename para decirle al comstackdor que lo typename de cierta manera. El estándar dice en (14.6 / 2):

Se supone que un nombre utilizado en una statement o definición de plantilla y que depende de un parámetro de plantilla no nombra un tipo a menos que la búsqueda de nombre aplicable encuentre un nombre de tipo o el nombre esté calificado por la palabra clave typename.

Hay muchos nombres para los que typename no es necesario, porque el comstackdor puede, con la búsqueda de nombres aplicable en la definición de la plantilla, descubrir cómo analizar una construcción en sí misma, por ejemplo con T *f; , cuando T es un parámetro de plantilla de tipo. Pero para t::x * f; para ser una statement, debe escribirse como typename t::x *f; . Si omite la palabra clave y se toma el nombre como no de tipo, pero cuando la creación de instancias encuentra que denota un tipo, el comstackdor emite los mensajes de error habituales. A veces, el error en consecuencia se da en el momento de la definición:

 // t::x is taken as non-type, but as an expression the following misses an // operator between the two names or a semicolon separating them. t::xf; 

La syntax permite el typename solo antes de los nombres calificados , por lo que se considera que los nombres no calificados siempre se conocen para referirse a tipos si lo hacen.

Existe una idea similar para los nombres que denotan plantillas, como lo sugiere el texto introductorio.

La palabra clave “plantilla”

¿Recuerda la cita inicial anterior y cómo el Estándar requiere un manejo especial para las plantillas también? Tomemos el siguiente ejemplo de aspecto inocente:

 boost::function< int() > f; 

Puede parecer obvio para un lector humano. No es así para el comstackdor. Imagine la siguiente definición arbitraria de boost::function f :

 namespace boost { int function = 0; } int main() { int f = 0; boost::function< int() > f; } 

¡Esa es en realidad una expresión válida! Utiliza el operador menor que para comparar boost::function contra cero ( int() ), y luego usa el operador mayor que para comparar el bool resultante contra f . Sin embargo, como bien podría saber, boost::function en la vida real es una plantilla, por lo que el comstackdor sabe (14.2 / 3):

Después de la búsqueda de nombres (3.4) encuentra que un nombre es un nombre de plantilla, si este nombre es seguido de un <, el

Ahora volvemos al mismo problema que con typename . ¿Qué sucede si aún no podemos saber si el nombre es una plantilla al analizar el código? Tendremos que insertar la template inmediatamente antes del nombre de la plantilla, como se especifica en 14.2/4 . Esto se ve así:

 t::template f(); // call a function template 

Los nombres de las plantillas no solo pueden aparecer después de a :: sino también después de a -> o . en un acceso de miembro de la clase. También debe insertar la palabra clave allí:

 this->template f(); // call a function template 

Dependencias

Para las personas que tienen libros de Standardese gruesos en su estantería y que quieren saber exactamente de lo que estaba hablando, hablaré un poco sobre cómo se especifica en el Estándar.

En las declaraciones de plantilla, algunas construcciones tienen diferentes significados según los argumentos de la plantilla que utilice para crear la plantilla: las expresiones pueden tener diferentes tipos o valores, las variables pueden tener diferentes tipos o las llamadas a funciones pueden terminar llamando a diferentes funciones. Generalmente, se dice que tales construcciones dependen de los parámetros de la plantilla.

El estándar define con precisión las reglas según si un constructo es dependiente o no. Los separa en grupos lógicamente diferentes: uno capta tipos, otro capta expresiones. Las expresiones pueden depender de su valor y / o su tipo. Así que tenemos, con ejemplos típicos añadidos:

  • Tipos dependientes (p. Ej .: un parámetro de plantilla de tipo T )
  • Expresiones dependientes del valor (p. Ej .: un parámetro de plantilla sin tipo N )
  • Expresiones dependientes del tipo (por ejemplo: un molde a un parámetro de plantilla de tipo (T)0 )

La mayoría de las reglas son intuitivas y se crean recursivamente: por ejemplo, un tipo construido como T[N] es un tipo dependiente si N es una expresión dependiente del valor o T es un tipo dependiente. Los detalles de esto pueden leerse en la sección (14.6.2/1 ) para tipos dependientes, (14.6.2.2) para expresiones dependientes del tipo y (14.6.2.3) para expresiones dependientes del valor.

Nombres dependientes

El estándar no está claro acerca de qué es exactamente un nombre dependiente . En una lectura simple (ya sabes, el principio de menor sorpresa), todo lo que define como un nombre dependiente es el caso especial para los nombres de funciones a continuación. Pero dado que claramente T::x también debe buscarse en el contexto de instanciación, también debe ser un nombre dependiente (afortunadamente, a partir de mediados de C ++ 14 el comité ha comenzado a buscar cómo solucionar esta confusa definición) .

Para evitar este problema, he recurrido a una interpretación simple del texto estándar. De todos los constructos que denotan tipos o expresiones dependientes, un subconjunto de ellos representa nombres. Esos nombres son por lo tanto “nombres dependientes”. Un nombre puede tomar diferentes formas: el Estándar dice:

Un nombre es el uso de un identificador (2.11), operador-función-id (13.5), conversión-función-id (12.3.2), o plantilla-id (14.2) que denota una entidad o etiqueta (6.6.4, 6.1)

Un identificador es simplemente una secuencia simple de caracteres / dígitos, mientras que los siguientes dos son el operator + y el operator type . La última forma es template-name . Todos estos son nombres, y por uso convencional en el Estándar, un nombre también puede incluir calificadores que indiquen en qué espacio de nombres o clase debe buscarse un nombre.

Una expresión dependiente del valor 1 + N no es un nombre, pero N es. El subconjunto de todas las construcciones dependientes que son nombres se llama nombre dependiente . Los nombres de funciones, sin embargo, pueden tener un significado diferente en diferentes instancias de una plantilla, pero desafortunadamente no son atrapados por esta regla general.

Nombres de funciones dependientes

No es principalmente una preocupación de este artículo, pero aún vale la pena mencionarlo: los nombres de funciones son una excepción que se manejan por separado. Un nombre de función de identificador no depende de sí mismo, sino de las expresiones de argumento dependientes de tipo utilizadas en una llamada. En el ejemplo f((T)0) , f es un nombre dependiente. En el Estándar, esto se especifica en (14.6.2/1) .

Notas y ejemplos adicionales

En suficientes casos, necesitamos ambos typename de typename y template . Tu código debería verse como el siguiente

 template  struct UnionNode : public Tail { // ... template struct inUnion { typedef typename Tail::template inUnion dummy; }; // ... }; 

La template palabra clave no siempre tiene que aparecer en la última parte de un nombre. Puede aparecer en el medio antes de un nombre de clase que se usa como ámbito, como en el siguiente ejemplo

 typename t::template iterator::value_type v; 

En algunos casos, las palabras clave están prohibidas, como se detalla a continuación

  • En el nombre de una clase base dependiente, no está permitido escribir typename . Se supone que el nombre dado es un nombre de tipo de clase. Esto es cierto para ambos nombres en la lista de clase base y la lista de inicializadores del constructor:

      template  struct derive_from_Has_type : /* typename */ SomeBase::type { }; 
  • En el uso de declaraciones no es posible utilizar la template después de la última :: , y el comité de C ++ dijo que no trabaje en una solución.

      template  struct derive_from_Has_type : SomeBase { using SomeBase::template type; // error using typename SomeBase::type; // typename *is* allowed }; 

C ++ 11

Problema

Si bien las reglas en C ++ 03 sobre cuándo se necesita el typename y la template son en gran medida razonables, existe una desventaja molesta de su formulación

 template struct A { typedef int result_type; void f() { // error, "this" is dependent, "template" keyword needed this->g(); // OK g(); // error, "A" is dependent, "typename" keyword needed A::result_type n1; // OK result_type n2; } template void g(); }; 

Como se puede ver, necesitamos la palabra clave desambiguación incluso si el comstackdor pudiera A::result_type a sí mismo que A::result_type solo puede ser int (y por lo tanto un tipo), y this->g solo puede ser la plantilla de miembro g declarada después (incluso si A está explícitamente especializado en alguna parte, eso no afectaría el código dentro de esa plantilla, por lo que su significado no puede verse afectado por una especialización posterior de A !).

Instanciación actual

Para mejorar la situación, en C ++ 11 el idioma rastrea cuando un tipo se refiere a la plantilla adjunta. Para saberlo, el tipo debe haberse formado usando una cierta forma de nombre, que es su propio nombre (en lo anterior, A , A , ::A ). Se sabe que un tipo al que se hace referencia mediante dicho nombre es la creación de instancias actual . Puede haber múltiples tipos que son todas las instancias actuales si el tipo del que se forma el nombre es una clase miembro / anidada (entonces, A::NestedClass y A son ambas instancias actuales).

En base a esta noción, el lenguaje dice que CurrentInstantiation::Foo , Foo y CurrentInstantiationTyped->Foo (como A *a = this; a->Foo ) son todos miembros de la instanciación actual si se descubre que son miembros de un clase que es la instanciación actual o una de sus clases base no dependientes (simplemente haciendo la búsqueda de nombre inmediatamente).

Las palabras clave typename y template ya no son necesarias si el calificador es miembro de la creación de instancias actual. Un punto clave aquí para recordar es que A sigue siendo un nombre dependiente del tipo (después de todo, T también depende del tipo). Pero se sabe que A::result_type es un tipo: el comstackdor buscará “mágicamente” este tipo de tipos dependientes para resolverlo.

 struct B { typedef int result_type; }; template struct C { }; // could be specialized! template struct D : B, C { void f() { // OK, member of current instantiation! // A::result_type is not dependent: int D::result_type r1; // error, not a member of the current instantiation D::questionable_type r2; // OK for now - relying on C to provide it // But not a member of the current instantiation typename D::questionable_type r3; } }; 

Eso es impresionante, pero ¿podemos hacerlo mejor? El lenguaje incluso va más allá y requiere que una implementación vuelva a buscar D::result_type al crear instancias de D::f (incluso si ya encontró su significado en el momento de la definición). Cuando ahora el resultado de la búsqueda difiere o resulta en ambigüedad, el progtwig está mal formado y se debe dar un diagnóstico. Imagina lo que sucede si definimos C como esta

 template<> struct C { typedef bool result_type; typedef int questionable_type; }; 

Se requiere un comstackdor para detectar el error al crear instancias de D::f . Así que obtienes lo mejor de los dos mundos: búsqueda “retrasada” que te protegerá si pudieras tener problemas con las clases base dependientes, y también una búsqueda “Inmediata” que te libera del typename y la template .

Especializaciones desconocidas

En el código de D , el nombre typename D::questionable_type no es miembro de la typename D::questionable_type instancias actual. En cambio, el lenguaje lo marca como miembro de una especialización desconocida . En particular, este es siempre el caso cuando está haciendo DependentTypeName::Foo o DependentTypedName->Foo y el tipo dependiente no es la instanciación actual (en cuyo caso el comstackdor puede darse por vencido y decir “veremos más adelante qué es Foo ) o es la creación de instancias actual y el nombre no se encontró en él o sus clases base no dependientes y también hay clases base dependientes.

Imagine lo que sucede si tuviéramos una función miembro h dentro de la plantilla de clase A definida anteriormente

 void h() { typename A::questionable_type x; } 

En C ++ 03, el lenguaje permitió detectar este error porque nunca podría existir una forma válida de instanciar A::h (cualquiera que sea el argumento que le dé a T ). En C ++ 11, el lenguaje ahora tiene una verificación adicional para dar más razones para que los comstackdores implementen esta regla. Como A no tiene clases base dependientes, y A no declara ningún miembro questionable_type , el nombre A::questionable_type no es miembro de la creación de instancias actual ni miembro de una especialización desconocida. En ese caso, no debería haber forma de que ese código pueda comstackrse válidamente en el momento de instanciación, por lo que el lenguaje prohíbe un nombre donde el calificador es la instanciación actual para no ser miembro de una especialización desconocida ni miembro de la creación de instancias actual (sin embargo , esta violación aún no se requiere para ser diagnosticada).

Ejemplos y curiosidades

Puede probar este conocimiento sobre esta respuesta y ver si las definiciones anteriores tienen sentido para usted en un ejemplo del mundo real (se repiten un poco menos detalladas en esa respuesta).

Las reglas de C ++ 11 hacen que el siguiente código válido de C ++ 03 esté mal formado (lo cual no fue pensado por el comité de C ++, pero probablemente no será arreglado)

 struct B { void f(); }; struct A : virtual B { void f(); }; template struct C : virtual B, T { void g() { this->f(); } }; int main() { C c; cg(); } 

Este código válido de C ++ 03 vincularía this->f a A::f en el momento de creación de instancias y todo está bien. Sin embargo, C ++ 11 inmediatamente lo vincula a B::f y requiere una doble verificación al crear instancias, verificando si la búsqueda aún coincide. Sin embargo, al crear instancias de C::g , se aplica la regla de dominación y la búsqueda encontrará A::f lugar.

PREFACIO

Esta publicación pretende ser una alternativa fácil de leer a la publicación de litb .

El propósito subyacente es el mismo; una explicación a “¿Cuándo?” ¿y por qué?” typename y la template deben ser aplicados.


¿Cuál es el propósito de typename y template ?

typename y la template pueden usar en circunstancias que no sean al declarar una plantilla.

Hay ciertos contextos en C ++ donde se debe explicar explícitamente al comstackdor cómo tratar un nombre, y todos estos contextos tienen algo en común; ellos dependen de al menos un parámetro de plantilla .

Nos referimos a tales nombres, donde puede haber una ambigüedad en la interpretación, como; ” nombres dependientes “.

Esta publicación ofrecerá una explicación de la relación entre los nombres dependientes y las dos palabras clave.


UN SNIPPET DICE MÁS DE 1000 PALABRAS

Intente explicar lo que está sucediendo en la siguiente plantilla de función , ya sea para usted, un amigo o tal vez su gato; ¿Qué está sucediendo en la statement marcada ( A )?

 template void f_tmpl () { T::foo * x; /* <-- (A) */ } 


Puede que no sea tan fácil como uno piensa, más específicamente el resultado de la evaluación ( A ) depende en gran medida de la definición del tipo pasado como parámetro-plantilla T

Diferentes T s pueden cambiar drásticamente la semántica involucrada.

 struct X { typedef int foo; }; /* (C) --> */ f_tmpl (); struct Y { static int const foo = 123; }; /* (D) --> */ f_tmpl (); 


Los dos escenarios diferentes :

  • Si instanciamos la plantilla de función con tipo X , como en ( C ), tendremos una statement de un puntero-int llamado x , pero;

  • si instanciamos la plantilla con el tipo Y , como en ( D ), ( A ) consistiría en cambio en una expresión que calcula el producto de 123 multiplicado por alguna variable ya declarada x .


LO RACIONAL

El estándar C ++ se preocupa por nuestra seguridad y bienestar, al menos en este caso.

Para evitar que una implementación sufra sorpresas desagradables, el Estándar exige que solucionemos la ambigüedad de un nombre dependiente indicando explícitamente la intención en cualquier lugar donde deseamos tratar el nombre como un nombre de tipo , o una plantilla, id .

Si no se indica nada, se considerará que el nombre dependiente es una variable o una función.


¿CÓMO MANEJAR NOMBRES DEPENDIENTES ?

Si se tratara de una película de Hollywood, los nombres dependientes serían la enfermedad que se propaga a través del contacto corporal, afecta instantáneamente a su anfitrión para confundirlo. Confusión que podría, posiblemente, conducir a un progtwig perso- erhm .. mal formado.

Un nombre dependiente es cualquier nombre que depende directa o indirectamente de un parámetro de plantilla .

 template void g_tmpl () { SomeTrait::type foo; // (E), ill-formed SomeTrait::NestedTrait::type bar; // (F), ill-formed foo.data (); // (G), ill-formed } 

Tenemos cuatro nombres dependientes en el fragmento de arriba:

  • E )
    • "tipo" depende de la SomeTrait de instancias de SomeTrait , que incluyen T , y;
  • F )
    • "NestedTrait" , que es una plantilla-id , depende de SomeTrait y;
    • "tipo" al final de ( F ) depende de NestedTrait , que depende de SomeTrait , y;
  • G )
    • "datos" , que se parece a una plantilla de función de miembro , es indirectamente un nombre dependiente ya que el tipo de foo depende de la instanciación de SomeTrait .

Ninguno de los enunciados ( E ), ( F ) o ( G ) es válido si el comstackdor interpreta los nombres dependientes como variables / funciones (que, como se dijo anteriormente, es lo que ocurre si no se dice explícitamente lo contrario).

LA SOLUCIÓN

Para hacer que g_tmpl tenga una definición válida debemos decirle explícitamente al comstackdor que esperamos un tipo en ( E ), una plantilla-id y un tipo en ( F ), y una plantilla-id en ( G ).

 template void g_tmpl () { typename SomeTrait::type foo; // (G), legal typename SomeTrait::template NestedTrait::type bar; // (H), legal foo.template data (); // (I), legal } 

Cada vez que un nombre denota un tipo, todos los nombres involucrados deben ser nombres de tipos o espacios de nombres , con esto en mente, es bastante fácil ver que aplicamos el typename al principio de nuestro nombre completo.

Sin embargo, la template es diferente a este respecto, ya que no hay forma de llegar a una conclusión como; "oh, esta es una plantilla, que esta otra cosa también debe ser una plantilla" . Esto significa que aplicamos la template directamente delante de cualquier nombre que nos gustaría tratar como tal.


¿PUEDO APENAS PEGAR LAS PALABRAS CLAVE FRENTE A CUALQUIER NOMBRE?

" ¿Puedo typename y la template delante de cualquier nombre? No quiero preocuparme por el contexto en el que aparecen ... " - Some C++ Developer

Las reglas de la Norma establecen que puede aplicar las palabras clave siempre que se trate de un nombre calificado ( K ), pero si el nombre no está calificado, la aplicación está mal formada ( L ).

 namespace N { template struct X { }; } 

  N:: X a; // ... legal typename N::template X b; // (K), legal typename template X c; // (L), ill-formed 

Nota : La aplicación de typename o template en un contexto donde no se requiere no se considera una buena práctica; solo porque puedes hacer algo, no significa que debas hacerlo.

Además, hay contextos donde typename y template están explícitamente desautorizados:

  • Al especificar las bases de las cuales una clase hereda

    Cada nombre escrito en la base-especificador-lista de una clase derivada ya se trata como un nombre de tipo , especificando explícitamente que typename está mal formado y redundante.

      // .------- the base-specifier-list template // v struct Derived : typename SomeTrait::type /* <- ill-formed */ { ... }; 

  • Cuando el template-id es el referido en la directiva de uso de una clase derivada

      struct Base { template struct type { }; }; struct Derived : Base { using Base::template type; // ill-formed using Base::type; // legal }; 
 typedef typename Tail::inUnion dummy; 

Sin embargo, no estoy seguro de que la implementación de inUnion sea correcta. Si entiendo correctamente, se supone que esta clase no debe crearse una instancia, por lo tanto, la pestaña “fallar” nunca fallará. Tal vez sería mejor indicar si el tipo está en la unión o no con un valor booleano simple.

 template  struct Contains; template  struct Contains > { enum { result = Contains::result }; }; template  struct Contains > { enum { result = true }; }; template  struct Contains { enum { result = false }; }; 

PD: echa un vistazo a Boost :: Variant

PS2: Eche un vistazo a las listas de tipos , especialmente en el libro de Andrei Alexandrescu: Modern C ++ Design

Esta respuesta debe ser bastante corta y dulce para responder (parte de) la pregunta titulada. Si quieres una respuesta con más detalles que explique por qué tienes que ponerlos allí, ve aquí .


La regla general para poner la palabra clave typename es principalmente cuando está usando un parámetro de plantilla y desea acceder a un typedef o alias-alias nesteds, por ejemplo:

 template struct test { using type = T; // no typename required using underlying_type = typename T::type // typename required }; 

Tenga en cuenta que esto también se aplica a las metafunciones o cosas que también toman los parámetros generics de la plantilla. Sin embargo, si el parámetro de plantilla proporcionado es un tipo explícito, entonces no tiene que especificar typename , por ejemplo:

 template struct test { // typename required using type = typename std::conditional::type; // no typename required using integer = std::conditional::type; }; 

Las reglas generales para agregar el calificador de template son en su mayoría similares, excepto que normalmente implican funciones de miembros con plantilla (estáticas o de otro tipo) de una estructura / clase que está templada, por ejemplo:

Dada esta estructura y función:

 template struct test { template void get() const { std::cout << "get\n"; } }; template void func(const test& t) { t.get(); // error } 

Si intenta acceder a t.get() desde el interior de la función, se producirá un error:

 main.cpp:13:11: error: expected primary-expression before 'int' t.get(); ^ main.cpp:13:11: error: expected ';' before 'int' 

Por lo tanto, en este contexto necesitaría la template palabra clave de antemano y llámala así:

t.template get()

De esta forma, el comstackdor analizará esto correctamente en lugar de t.get < int .

Estoy colocando la respuesta excelente de JLBorges a una pregunta similar textualmente de cplusplus.com, ya que es la explicación más sucinta que he leído sobre el tema.

En una plantilla que escribimos, hay dos tipos de nombres que podrían usarse: nombres dependientes y nombres no dependientes. Un nombre dependiente es un nombre que depende de un parámetro de plantilla; un nombre no dependiente tiene el mismo significado, independientemente de cuáles sean los parámetros de la plantilla.

Por ejemplo:

 template< typename T > void foo( T& x, std::string str, int count ) { // these names are looked up during the second phase // when foo is instantiated and the type T is known x.size(); // dependant name (non-type) T::instance_count ; // dependant name (non-type) typename T::iterator i ; // dependant name (type) // during the first phase, // T::instance_count is treated as a non-type (this is the default) // the typename keyword specifies that T::iterator is to be treated as a type. // these names are looked up during the first phase std::string::size_type s ; // non-dependant name (type) std::string::npos ; // non-dependant name (non-type) str.empty() ; // non-dependant name (non-type) count ; // non-dependant name (non-type) } 

A qué se refiere un nombre dependiente podría ser algo diferente para cada instanciación diferente de la plantilla. Como consecuencia, las plantillas de C ++ están sujetas a “búsqueda de nombres de dos fases”. Cuando una plantilla se analiza inicialmente (antes de que se realice una instanciación), el comstackdor busca los nombres no dependientes. Cuando tiene lugar una instanciación particular de la plantilla, los parámetros de la plantilla son conocidos para entonces, y el comstackdor busca nombres dependientes.

Durante la primera fase, el analizador necesita saber si un nombre dependiente es el nombre de un tipo o el nombre de un no-tipo. De forma predeterminada, se supone que un nombre dependiente es el nombre de un no tipo. La palabra clave typename antes de un nombre dependiente especifica que es el nombre de un tipo.


Resumen

Use la palabra clave typename solo en declaraciones de plantilla y definiciones siempre que tenga un nombre calificado que haga referencia a un tipo y que dependa de un parámetro de plantilla.