2009-08-08 6 views
10

estaba leyendo el artículo de Wikipedia sobre SFINAE y se encontró siguiente ejemplo de código:¿Por qué a veces necesitas escribir `typename T` en lugar de simplemente` T`?

struct Test 
{ 
    typedef int Type; 
}; 

template < typename T > 
void f(typename T::Type) {} // definition #1 

template < typename T > 
void f(T) {}    // definition #2 

void foo() 
{ 
    f<Test> (10); //call #1 

    f<int> (10); //call #2 without error thanks to SFINAE 
} 

Ahora he hecho código escrito como esto antes, y de alguna manera intuitivamente sabía que tenía que escribir "nombre de tipo T" en lugar de solo "T". Sin embargo, sería bueno saber la lógica real detrás de esto. ¿Alguien quiere explicar?

+1

Te recomiendo que leas la plantilla de preguntas frecuentes: http://womble.decadentplace.org.uk/c++/template-faq.html –

Respuesta

8

En general, la sintaxis de C++ (heredada de C) tiene un defecto técnico: el analizador DEBE saber si algo nombra un tipo, o no, de lo contrario simplemente no puede resolver ciertas ambigüedades (por ejemplo, es X * Y una multiplicación, o la declaración de un puntero Y a objetos de tipo X? todo depende de si X nombra un tipo ...! -). El "adjetivo" typename le permite hacer eso perfectamente claro y explícito cuando sea necesario (que, como menciona otra respuesta, es típico cuando se trata de parámetros de la plantilla ;-).

+1

¿Quiere decir "normalmente cuando se trata de parámetros de plantilla" (en cuyo caso estoy bastante seguro de que es * solo * cuando se trata de parámetros de plantilla), o es un error tipográfico "que es típico cuando se trata de parámetros de plantilla" ? –

+1

@onebyone, buena captura, realmente quise decir "típico" y no "típicamente", edité mi respuesta para corregir, tx. –

13

La versión corta que necesita hacer typename X::Y cuando X es o depende de un parámetro de plantilla. Hasta que se conozca X, el compilador no puede decir si Y es un tipo o un valor. Por lo tanto, debe agregar typename para especificar que es un tipo.

Por ejemplo:

template <typename T> 
struct Foo { 
    typename T::some_type x; // T is a template parameter. `some_type` may or may not exist depending on what type T is. 
}; 

template <typename T> 
struct Foo { 
    typename some_template<T>::some_type x; // `some_template` may or may not have a `some_type` member, depending on which specialization is used when it is instantiated for type `T` 
}; 

Como sbi señala en los comentarios, la causa de la ambigüedad es que Y podría ser un miembro estático, una enumeración o una función. Sin saber el tipo de X, no podemos decirlo. El estándar especifica que el compilador debe suponer que es un valor a menos que esté explícitamente etiquetado como un tipo mediante la palabra clave typename.

Y parece que los comentaristas realmente quieren que mencione otro caso relacionado, así:;)

Si el nombre del dependiente es una plantilla miembro de la función, y se le llama con un argumento de plantilla explícita (foo.bar<int>(), por ejemplo), debe agregar la palabra clave template antes del nombre de la función, como en foo.template bar<int>().

La razón de esto es que sin la palabra clave de la plantilla, el compilador supone que bar es un valor, y desea invocar al operador menor que (operator<) en él.

+0

jalf, podría valer la pena mencionar un significado alternativo de 'T :: something' (datos estáticos, nombre de la función). Y una vez que lo hagas tan bien, podría valer la pena agregar la explicación de por qué a veces también debemos inyectar una 'plantilla'. Tiene el mismo motivo, por lo que no tardará mucho más. – sbi

+0

'template' no es el mismo razonamiento. No tiene nada que ver con los nombres dependientes. Cae en la categoría de "problemas que te hacen tropezar con plantillas", pero no creo que sea la misma razón que con 'typename'. – jalf

+1

@jalf: ¿No tiene que ver con los nombres de las plantillas? En cualquier caso, las razones son bastante análogas. Solo está permitido en el código de la plantilla y debe usarse para distinguir nombres de miembros dependientes que son nombres de plantillas de nombres de miembros dependientes que no lo son. –

3

Básicamente, necesita la palabra clave typename cuando escribe código de plantilla (es decir, se encuentra en una plantilla de función o plantilla de clase) y se refiere a un identificador que depende de un parámetro de plantilla que podría no ser conocido como escriba, pero debe interpretarse como un tipo en su código de plantilla.

En su ejemplo, usa typename T::Type en la definición n. ° 1 porque T::Type depende del parámetro de plantilla T y podría ser un miembro de datos.

No necesita typename T en la definición # 2 ya que T se declara como un tipo como parte de la definición de la plantilla.

Cuestiones relacionadas