2011-10-27 25 views
43

Duplicar posible:
Officially, what is typename for?
Where and why do I have to put the template and typename keywords?¿Cuándo es necesaria la palabra clave "typename"?

consideran el código de abajo:

template<class K> 
class C { 
    struct P {}; 
    vector<P> vec; 
    void f(); 
}; 

template<class K> void C<K>::f() { 
    typename vector<P>::iterator p = vec.begin(); 
} 

¿Por qué es la palabra clave "nombre de tipo" necesaria en este ejemplo? ¿Hay algún otro caso donde se deba especificar "typename"?

+0

http://stackoverflow.com/questions/610245/where-and-why-do- i-have-to-put-template-and-typename-on-dependent-names –

Respuesta

58

Respuesta corta: siempre que se haga referencia a un nombre anidado que es nombre dependiente, es decir, anidado en una instancia de plantilla con parámetro desconocido.

Respuesta larga: Hay tres niveles de entidades en C++: valores, tipos y plantillas. Todos esos pueden tener nombres, y el nombre solo no le dice qué nivel de entidad es. Por el contrario, la información sobre la naturaleza de la entidad de un nombre debe inferirse del contexto.

Siempre que esta inferencia es imposible, tiene que especificar que:

template <typename> struct Magic; // defined somewhere else 

template <typename T> struct A 
{ 
    static const int value = Magic<T>::gnarl; // assumed "value" 

    typedef typename Magic<T>::brugh my_type; // decreed "type" 
    //  ^^^^^^^^ 

    void foo() { 
    Magic<T>::template kwpq<T>(1, 'a', .5); // decreed "template" 
    //  ^^^^^^^^ 
    } 
}; 

Aquí los nombres Magic<T>::gnarl, Magic<T>::brugh y Magic<T>::kwpq tuvo que ser expliciated, porque es imposible saber: Desde Magic es una plantilla, el muy naturaleza del tipo Magic<T> depende de T - puede haber especializaciones que son completamente diferentes de la plantilla principal, por ejemplo.

Lo que hace que Magic<T>::gnarl sea un nombre dependiente es el hecho de que estamos dentro de una definición de plantilla, donde T es desconocido. Si hubiéramos usado Magic<int>, esto sería diferente, ya que el compilador lo sabe (¡lo promete!) la definición completa de Magic<int>.

(Si desea probar esto por sí mismo, he aquí una definición de ejemplo para Magic que puede utilizar Perdón por la utilización de constexpr en el specializaation por razones de brevedad;. Si usted tiene un viejo compilador, no dude en cambiar la constante de miembro estático declaración a la pre-C forma de estilo antiguo ++ 11)

template <typename T> struct Magic 
{ 
    static const T     gnarl; 
    typedef T &      brugh; 
    template <typename S> static void kwpq(int, char, double) { T x; } 
}; 
template <> struct Magic<signed char> 
{ 
    // note that `gnarl` is absent 
    static constexpr long double brugh = 0.25; // `brugh` is now a value 
    template <typename S> static int kwpq(int a, int b) { return a + b; } 
}; 

Uso:.

int main() 
{ 
    A<int> a; 
    a.foo(); 

    return Magic<signed char>::kwpq<float>(2, 3); // no disambiguation here! 
} 
+0

@Nils: 'const' no sería válido C++. –

+0

Gracias por la explicación. Pero no puedo entender una última cosa: como el nombre 'Magic :: kwpq' depende del parámetro de plantilla' T', esto se resolverá en la segunda fase de la * búsqueda de dos fases *, ¿verdad? Entonces, ¿por qué el compilador no debería poder verificar en ese momento (cuándo podrá determinar si 'kwpq' es una plantilla o un tipo o lo que sea) si el nombre se usa correctamente según su * nivel de entidad *? –

+0

@PaoloM: la definición de la función miembro de la plantilla de clase 'A' tiene que analizarse ya durante la primera fase, por lo que necesita la desambiguación para que la sintaxis tenga sentido. En la segunda fase, comprobamos que la instanciación 'Magic ' para el tipo concreto 'T' en realidad tiene un miembro' kwpq' que es una función con la firma apropiada. (¡Y gracias por la edición!) –

7

Se necesita la palabra clave nombretipo cada vez que un nombre de tipo depende de un parámetro de plantilla, (por lo que el compilador puede 'saber' la semántica de un identificador (tipo o valor) sin tener una tabla de símbolos completo en la primera pasar).


No

en el mismo sentido, y un poco menos común, el solitaria palabra clave nombretipo también puede ser útil cuando se utilizan los parámetros de plantilla genéricos: http://ideone.com/amImX

#include <string> 
#include <list> 
#include <vector> 

template <template <typename, typename> class Container, 
      template <typename> class Alloc = std::allocator> 
struct ContainerTests 
{ 
    typedef Container<int, Alloc<int> > IntContainer; 
    typedef Container<std::string, Alloc<int> > StringContainer; 
    // 
    void DoTests() 
    { 
     IntContainer ints; 
     StringContainer strings; 
     // ... etc 
    } 
}; 

int main() 
{ 
    ContainerTests<std::vector> t1; 
    ContainerTests<std::list> t2; 

    t1.DoTests(); 
    t2.DoTests(); 
} 
+0

Nunca supe ab fuera "lone typename" antes, ¡eso es genial! –

10

El typename de palabras clave, es necesario porque iterator es un tipo dependiente en P. El compilador no puede adivinar si iterator se refiere a un valor o un tipo, por lo que asume que es un valor a menos que grite typename. Se necesita siempre que haya un tipo que dependa de un argumento de plantilla, en un contexto en el que los tipos o valores serían válidos. Por ejemplo, como clases base typename no es necesario ya que una clase base debe ser un tipo.

En el mismo tema, hay una palabra clave template que se usa para que el compilador sepa que algún nombre dependiente es una función de plantilla en lugar de un valor.

Cuestiones relacionadas