Ir para o conteúdo

Última atualização:2026-10-12

Análise do código-fonte do STL typename

O código-fonte de baixo nível da STL contém as linhas abaixo, com typedef usado em conjunto com typename; parecem bem complexas — o que afinal significam? Vamos analisar isso juntos hoje!

template<typename _Iterator>
struct iterator_traits
{
  typedef typename _Iterator::iterator_category iterator_category;
  typedef typename _Iterator::value_type        value_type;
  typedef typename _Iterator::difference_type   difference_type;
  typedef typename _Iterator::pointer           pointer;
  typedef typename _Iterator::reference         reference;
};

Usos comuns de typename

Primeiro, vamos aprender os usos comuns de typename:

template <typename T>
int compare(const T &a, const T &b)
{
    return a>b?a:b;
}

O acima é apenas um programa de exemplo; para escrever uma comparação completa de tamanho, seria preciso considerar versões especializadas. Talvez você perceba que trocar o typename do código acima por class também funciona — muito bem! Surge então a dúvida: há diferença entre as duas formas? Consultando o C++ Primer, descobrimos que são exatamente iguais.

Escopo de classe

Ao acessar nomes de dentro de uma classe fora dela, pode-se usar o operador de escopo de classe. Chamadas da forma MyClass::name normalmente se enquadram em três casos: dados-membro estáticos, funções-membro estáticas e tipos aninhados:

struct MyClass {
    static int A; //静态成员
    static int B(){cout<<"B()"<<endl; return 100;} //静态函数
    typedef int C;  //嵌套类型
    struct A1 { //嵌套类型
        static int s;
    };
};

Na chamada, pode-se chamar diretamente:

cout<<MyClass::A<<endl;
cout<<MyClass::B()<<endl;
MyClass:C c;
...

Tentando um exemplo completo

Voltemos a um exemplo com typename:

template <class T>
void foo() {
    T::iterator * iter;
    // ...
}

Qual é o propósito deste código? A primeira reação da maioria das pessoas pode ser: o autor quer definir um ponteiro iter, cujo tipo apontado é o iterator contido no escopo da classe T. Pode existir uma estrutura assim, contendo o tipo iterator:

struct MyIterator {
    struct iterator {

    };
};

A chamada é a seguinte:

foo<MyIterator>();

Dessa forma, a linha de código com iter fica evidente: é um ponteiro para o tipo MyIterator::iterator. Supomos que seja isso — mas será que é?

Porém, e se for algo como T::iterator? T é um parâmetro de tipo no template; só se saberá de qual tipo se trata quando o template for instanciado, sem falar no iterator interno. Pela introdução anterior ao escopo de classe, sabemos que T::iterator pode, na verdade, ser qualquer um destes três tipos:

  • dado-membro estático
  • função-membro estática
  • tipo aninhado

O ContainsAType::iterator do exemplo anterior é um tipo aninhado, sem problema algum. Mas e se for um dado-membro estático? Se o tipo usado para instanciar a função de template foo for assim:

struct MyIterator {
    static int iterator;
};

Então T::iterator * iter; é instanciado pelo compilador como MyIterator::iterator * iter; — o que é isso? À frente há uma variável-membro estática, e não um tipo; isso se torna uma expressão de multiplicação, só que iter não está definido aqui, e o compilador reportará um erro:

error: no type named ‘iterator’ in ‘struct MyIterator’

typename

Para nomes usados em definições de templates que dependem de parâmetros de template, o compilador só tratará o nome como um tipo se o tipo existir nos parâmetros da instanciação, ou se o nome for precedido da palavra-chave typename. Fora esses dois casos, jamais será tratado como tipo.

Portanto, se você quiser dizer diretamente ao compilador que T::iterator é um tipo, e não uma variável, basta qualificá-lo com typename:

template <class T>
void foo() {
    typename T::iterator * iter;
}

Assim, o compilador consegue determinar que T::iterator é um tipo, sem precisar esperar até a instanciação, eliminando a ambiguidade mencionada antes.

Analisando o código-fonte

De volta ao código-fonte da STL

template<typename _Iterator>
struct iterator_traits
{
  typedef typename _Iterator::iterator_category iterator_category;
  typedef typename _Iterator::value_type        value_type;
  typedef typename _Iterator::difference_type   difference_type;
  typedef typename _Iterator::pointer           pointer;
  typedef typename _Iterator::reference         reference;
};

Vendo o acima, fica tudo claro de imediato: nada mais é do que usar typename para dizer ao compilador que _Iterator::iterator_category é um tipo e então renomeá-lo com typedef; os demais são análogos!

Tópicos relacionados

Comentários