¿Diferencia de palabras clave 'typename' y 'class' en las plantillas?

504

Para las plantillas, he visto ambas declaraciones:

template < typename T >
template < class T >

¿Cual es la diferencia?

¿Y qué significan exactamente esas palabras clave en el siguiente ejemplo (tomado del artículo de Wikipedia en alemán sobre plantillas)?

template < template < typename, typename > class Container, typename Type >
class Example
{
     Container< Type, std::allocator < Type > > baz;
};
Estera
fuente

Respuestas:

430

typenamey classson intercambiables en el caso básico de especificar una plantilla:

template<class T>
class Foo
{
};

y

template<typename T>
class Foo
{
};

son equivalentes

Dicho esto, hay casos específicos en los que hay una diferencia entre typenamey class.

El primero es en el caso de los tipos dependientes. typenamese usa para declarar cuando se hace referencia a un tipo anidado que depende de otro parámetro de plantilla, como typedefen este ejemplo:

template<typename param_t>
class Foo
{
    typedef typename param_t::baz sub_t;
};

El segundo que realmente muestra en su pregunta, aunque es posible que no se dé cuenta:

template < template < typename, typename > class Container, typename Type >

Al especificar una plantilla de la plantilla , la classpalabra clave debe ser utilizado como anteriormente - es no intercambiable con typenameen este caso (nota: puesto que C ++ 17 ambas palabras clave se les permite en este caso) .

También debe usar classcuando crea una instancia explícitamente de una plantilla:

template class Foo<int>;

Estoy seguro de que hay otros casos que me he perdido, pero la conclusión es: estas dos palabras clave no son equivalentes, y estos son algunos casos comunes en los que necesita usar uno u otro.

Aaron Klotz
fuente
45
Ese último es más o menos un caso especial del hecho de que debe usar class o struct, no typename, para definir una clase. Obviamente, ninguno de sus dos primeros bits de código podría reemplazarse template <typename T> typename Foo {};, porque Foo <T> es definitivamente una clase.
Steve Jessop
2
std::vector<int>::value_typeno es un tipo dependiente, no lo necesita typenameallí, solo lo necesita si un tipo depende de un parámetro de plantilla, por ejemplotemplate<class T> struct C { typedef typename std::vector<T>::value_type type; };
Georg Fritzsche el
2
Y de nuevo, param_tno es un tipo dependiente. Los tipos dependientes son nombres que dependen de un parámetro de plantilla , por ejemplo foo<param_t>::some_type, no parámetros de plantilla en sí.
Georg Fritzsche
2
Una propuesta de C ++ 1z N4051 le permitirá usar typename, es decir template <typename> typename C.
user4112979
44
A partir de ahoraGCC 5 , G ++ ahora permite typename en un parámetro de plantilla de plantilla .
Chnossos
95

Para nombrar parámetros de plantilla, typenamey classson equivalentes. §14.1.2:

No hay diferencia semántica entre clase y nombre de tipo en un parámetro de plantilla.

typenamesin embargo, es posible en otro contexto cuando se usan plantillas, para insinuar al compilador que se está refiriendo a un tipo dependiente. §14.6.2:

Se supone que un nombre utilizado en una declaración 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 sea calificado por la palabra clave typename.

Ejemplo:

typename some_template<T>::some_type

Sin typenameel compilador no se puede decir en general si se refiere a un tipo o no.

Georg Fritzsche
fuente
2
Entiendo la regla, pero ¿qué impide exactamente que el compilador trate some_template <T> como un tipo interno? Lo siento si me falta algo obvio.
Batbrat
23

Si bien no existe una diferencia técnica, he visto que los dos denotan cosas ligeramente diferentes.

Para una plantilla que debe aceptar cualquier tipo como T, incluidas las incorporadas (como una matriz)

template<typename T>
class Foo { ... }

Para una plantilla que solo funcionará donde T es una clase real.

template<class T>
class Foo { ... }

Pero tenga en cuenta que esto es puramente un estilo que usan algunas personas. No es obligatorio según el estándar ni es exigido por los compiladores

Michael Anderson
fuente
15
No te culpo por mencionarlo, pero creo que esta política es bastante equivocada, ya que los programadores terminan tomando tiempo pensando en algo que no importa ("¿he usado el correcto?") Para indicar algo que no importa. no importa ("¿existe un tipo incorporado que implemente la interfaz requerida para este parámetro de plantilla?"). Si se utilizan los miembros del parámetro de plantilla ( T t; int i = t.toInt();), entonces necesita una "clase real", y su código no se compilará si se suministra intpara T...
Steve Jessop
1
Si desea limitar el uso a clases reales, es mejor agregar una especialización para lanzar / causar un error para los tipos que no son de clase. Si quieres limitar el uso a clases particulares, solo especialízate para ellas. En cualquier caso, esta distinción estilística es demasiado sutil para transmitir el mensaje.
Potatoswatter
2
Como significaban lo mismo, utilice solo uno. De lo contrario, es como usar inline {, a menos que sea un martes, y luego usar next-line {.
Paul Draper
+1 A veces lo hago yo mismo ... classimplica que no solo esperas un "valor" que tal vez respalde a algunos operadores, copie o mueva la construcción y / o asignación, sino que específicamente necesitas un tipo que soporte alguna semántica de acceso de miembros. El vistazo más rápido a la declaración establece expectativas y desalienta, por ejemplo, el suministro de tipos integrados para classparámetros cuando eso ciertamente sería un error.
Tony Delroy
Me gustaría entender qué tipos de situaciones del mundo real existen donde una plantilla funcionaría para CUALQUIER clase, pero no funcionaría con tipos incorporados. ¿Tienes un ejemplo?
lfalin
7
  1. Ninguna diferencia
  2. El parámetro de tipo de plantilla Containeres en sí mismo una plantilla con dos parámetros de tipo.
Nikolai Fetissov
fuente
3
Hay una diferencia en general.
Hassan Syed
¿Podrían nombrarse también esos dos parámetros con los que se crea la plantilla del contenedor? en el ejemplo no tienen ningún nombre. Y también, en este ejemplo, está escrito 'contenedor de clase', ¿podría también escribirse 'contenedor de nombre de tipo'?
Mat
2
@ Mat: sí, el término para buscar es parámetros / argumentos de plantilla de plantilla . Por ejemplo:template<template<class U> class V> struct C {};
Georg Fritzsche el
6

Este fragmento es del libro de c ++. Aunque estoy seguro de que esto está mal.

Cada parámetro de tipo debe ir precedido de la clase de palabra clave o nombre de tipo:

// error: must precede U with either typename or class
template <typename T, U> T calc(const T&, const U&);

Estas palabras clave tienen el mismo significado y se pueden usar indistintamente dentro de una lista de parámetros de plantilla. Una lista de parámetros de plantilla puede usar ambas palabras clave:

// ok: no distinction between typename and class in a template parameter list
template <typename T, class U> calc (const T&, const U&);

Puede parecer más intuitivo usar la palabra clave typename en lugar de class para designar un parámetro de tipo de plantilla. Después de todo, podemos usar tipos integrados (no de clase) como argumento de tipo de plantilla. Además, typename indica más claramente que el nombre que sigue es un nombre de tipo. Sin embargo, typename se agregó a C ++ después de que las plantillas ya estaban en uso generalizado; algunos programadores continúan usando la clase exclusivamente

KK
fuente