template e typename em C++: onde, por que e por que isso é importante

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

template e typename em C++: onde colocar e por que não dá para viver sem eles

\n\n

Se você já escreveu ou leu código C++ com templates, certamente já se deparou com uma situação em que o compilador, do nada, apresenta um erro. Você tem certeza de que o código está correto, mas o compilador insiste em exigir template ou typename. Familiar? Bem-vindo ao clube.

\n\n

Esta é uma das perguntas mais populares no Stack Overflow (milhares de visualizações e dezenas de respostas). E não é surpresa: até desenvolvedores experientes às vezes se confundem sobre onde usar essas palavras-chave. Neste artigo, vamos entender de uma vez por todas: onde, quando e por que escrever template e typename em C++. E o mais importante: entenderemos como não cair em armadilhas.

\n\n

Por que esse problema surgiu?

\n\n

A raiz do problema está nos nomes dependentes (dependent names). Quando você escreve código com template, o compilador não consegue entender imediatamente com o que está lidando: se é um tipo ou outra coisa. Isso porque os parâmetros do template ainda não são conhecidos. O compilador adia a análise até o momento da instanciação, mas precisa saber o papel sintático do nome já na fase de análise inicial.

\n\n

Imagine que você escreve:

\n\n
template<typename T>\nvoid foo() {\n    T::something * p;\n}\n
\n\n

O que é isso? Uma declaração do ponteiro p para T::something? Ou uma multiplicação de T::something por p? Sem informações adicionais, o compilador não sabe. E é exatamente para resolver essa ambiguidade que as palavras-chave typename e template foram introduzidas.

\n\n

A palavra-chave typename: quando o tipo não é óbvio

\n\n

Regra principal

\n\n

A palavra-chave typename é usada para dizer ao compilador: "Ei, o que vem a seguir é um tipo, e não outra coisa". Ela é necessária nos casos em que o nome depende de um parâmetro do template e o compilador não consegue determinar por conta própria que se trata de um tipo.

\n\n

Vejamos um exemplo clássico:

\n\n
template<typename T>\nclass MyClass {\npublic:\n    // Sem typename — erro de compilação\n    typename T::value_type getValue() const {\n        return data;\n    }\nprivate:\n    T data;\n};\n
\n\n

Aqui, T::value_type é um nome dependente. Sem typename, o compilador pensaria que value_type é um membro de dados estático ou uma classe aninhada, mas não um tipo. E nós sabemos que é um tipo (por exemplo, int, se T for std::vector<int>).

\n\n

Onde mais o typename é necessário?

\n\n

A regra é simples: typename é obrigatório antes de nomes de tipos dependentes nos seguintes contextos:

\n\n
    \n
  • Em declarações de variáveis e membros de classe
  • \n
  • Em parâmetros de funções
  • \n
  • Em tipos de retorno
  • \n
  • Em declarações using (apelidos de tipos)
  • \n
  • Em classes base (se a classe base for um tipo dependente)
  • \n
\n\n

Exemplo com using:

\n\n
template<typename T>\nclass Container {\n    using Iterator = typename T::iterator;  // typename é obrigatório!\n    // ...\n};\n
\n\n

Quando o typename NÃO é necessário?

\n\n

Existem exceções importantes em que typename não é necessário:

\n\n
    \n
  • Na lista de parâmetros do template: template<typename T> — aqui typename faz parte da sintaxe
  • \n
  • Em classes base: class Derived : public Base<T>::Nested — aqui typename não é necessário
  • \n
  • Na lista de inicialização do construtor
  • \n
  • Em contextos onde o nome é explicitamente um tipo (por exemplo, após new)
  • \n
\n\n

A palavra-chave template: quando o template está dentro do template

\n\n

A essência do problema

\n\n

Com template a situação é espelhada. Ela é necessária quando você acessa um membro que é um template den

Blogs

Book Recommendations