O que está por trás das palavras "typename" e "class"? Diferenças que você precisa saber
\n\nSe você já escreveu templates em C++, certamente já se deparou com duas palavras-chave aparentemente intercambiáveis: typename e class. Parece um detalhe, mas no Stack Overflow essa pergunta acumulou milhares de visualizações. Por quê? Porque por trás da aparente simplicidade existe uma armadilha que pode quebrar a compilação do seu código.
Neste artigo, vamos entender de uma vez por todas: qual a diferença entre typename e class em templates C++, quando podem ser usados como sinônimos e quando isso levará a um erro. Sem enrolação — apenas código, exemplos e regras claras.
\n\nResposta rápida: afinal, existe diferença?
\n\nA resposta mais popular no Stack Overflow é: para declarar parâmetros de template, não há diferença. Você pode escrever:
\n\ntemplate <typename T> void foo(T t);\nou
\ntemplate <class T> void foo(T t);\n\nAmbas as opções são totalmente válidas e fazem a mesma coisa: declaram um parâmetro de template T. Historicamente, class apareceu primeiro (no C++98), e typename foi adicionado depois para enfatizar que o parâmetro pode ser não apenas uma classe, mas qualquer tipo (int, double, ponteiro, etc.).
No entanto, o diabo está nos detalhes. Existe um cenário onde typename é obrigatório e class não funciona. E esse cenário são os nomes dependentes (dependent names).
A principal armadilha: tipos dependentes (dependent types)
\n\nImagine a situação: você está escrevendo um template e, dentro dele, acessa um tipo aninhado ao parâmetro do template. Por exemplo, você tem um contêiner e quer obter seu iterador:
\n\ntemplate <typename Container>\nvoid printFirst(const Container& c) {\n Container::const_iterator it = c.begin(); // ERRO!\n std::cout << *it;\n}\n\nPor que isso não compila? Porque Container::const_iterator é um nome dependente. O compilador, durante a análise sintática, não sabe o que é Container. Pode ser uma classe ou o nome de uma variável estática? Por padrão, o C++ assume que Container::const_iterator é o nome de uma variável (ou membro estático), e não um tipo. E apenas a palavra-chave typename diz ao compilador: "Ei, isso é um tipo!".
Corrigindo:
\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: aqui, class não funciona. Escrever class Container::const_iterator é um erro de sintaxe. typename é a única opção correta.
Quando usar typename em vez de class?
\n\nLembre-se da regra simples: typename é usado em dois casos:
- \n
- Como parâmetro de template (sinônimo de
class):template <typename T>\n - Para indicar que um nome dependente é um tipo:
typename T::value_type\n
Já class é usado apenas no primeiro caso. E muitos programadores C++ preferem typename para parâmetros, para não confundir com a declaração de uma classe.
Exemplo completo com explicações
\n\nVamos analisar um código real que demonstra a diferença entre typename e class em templates C++ na prática:
\n\n#include <iostream>\n#include <vector>\n#include <list>\n\n// Passo 1: template com class (ou typename — tanto faz)\ntemplate <class T>\nclass MyContainer {\npublic:\n // Passo 2: usamos typename para o tipo dependente\n // T::value_type é um tipo, não uma