¿Qué se esconde detrás de las palabras "typename" y "class"? Diferencias que debes conocer
\n\nSi alguna vez has escrito plantillas en C++, seguramente te has encontrado con dos palabras clave aparentemente intercambiables: typename y class. Parece un detalle menor, pero en Stack Overflow esta pregunta ha acumulado miles de visitas. ¿Por qué? Porque detrás de la aparente simplicidad se esconde una trampa que puede romper la compilación de tu código.
En este artículo, aclararemos de una vez por todas: ¿cuál es la diferencia entre typename y class en plantillas C++?, cuándo se pueden usar como sinónimos y cuándo esto provocará un error. Sin rodeos: solo código, ejemplos y reglas claras.
\n\nRespuesta breve: ¿realmente hay alguna diferencia?
\n\nLa respuesta más popular en Stack Overflow es: para declarar parámetros de plantilla, no hay diferencia. Puedes escribir:
\n\ntemplate <typename T> void foo(T t);\no
\ntemplate <class T> void foo(T t);\n\nAmbas opciones son completamente válidas y hacen lo mismo: declaran un parámetro de plantilla T. Históricamente, class apareció primero (ya en C++98), y typename se agregó después para enfatizar que el parámetro puede ser no solo una clase, sino cualquier tipo (int, double, puntero, etc.).
Sin embargo, el diablo está en los detalles. Hay un escenario donde typename es obligatorio y class no funciona. Y ese es el de los nombres dependientes (dependent names).
La principal trampa: tipos dependientes (dependent types)
\n\nImagina esta situación: estás escribiendo una plantilla y dentro de ella accedes a un tipo anidado en el parámetro de la plantilla. Por ejemplo, tienes un contenedor y quieres obtener su iterador:
\n\ntemplate <typename Container>\nvoid printFirst(const Container& c) {\n Container::const_iterator it = c.begin(); // ¡ERROR!\n std::cout << *it;\n}\n\n¿Por qué no compila? Porque Container::const_iterator es un nombre dependiente. El compilador, en la fase de análisis sintáctico, no sabe qué es Container. ¿Podría ser una clase? ¿O el nombre de una variable estática? Por defecto, C++ asume que Container::const_iterator es el nombre de una variable (o miembro estático de datos), no un tipo. Y solo la palabra clave typename le dice al compilador: "¡Oye, esto es un tipo!".
Lo corregimos:
\n\ntemplate <typename Container>\nvoid printFirst(const Container& c) {\n typename Container::const_iterator it = c.begin(); // ¡OK!\n std::cout << *it;\n}\n\nImportante: aquí class no sirve. Escribir class Container::const_iterator es un error de sintaxis. typename es la única opción correcta.
¿Cuándo usar typename en lugar de class?
\n\nRecuerda esta regla simple: typename se usa en dos casos:
- \n
- Como parámetro de plantilla (sinónimo de
class):template <typename T>\n - Para indicar que un nombre dependiente es un tipo:
typename T::value_type\n
Y class solo para el primer caso. Es más, muchos programadores de C++ prefieren typename para los parámetros, para no confundirlo con la declaración de una clase.
Ejemplo completo con explicaciones
\n\nAnalicemos un código real que demuestra la diferencia entre typename y class en plantillas C++ en la práctica:
\n\n#include <iostream>\n#include <vector>\n#include <list>\n\n// Paso 1: plantilla con class (o typename — da igual)\ntemplate <class T>\nclass MyContainer {\npublic:\n // Paso 2: usamos typename para el tipo dependiente\n // T::value_type es un tipo, no una