Templates parte 2

De ccppbrasil.org

Tabela de conteúdo

Templates parte II

Thiago Adams 27/3/06

No artigo anterior mostrei a nomeclatura usada em templates e o conceito de especializacao. Nesta segunda parte vou escrever sobre o conceito de Embedded Type Information e Traits/Police.
Estes conceitos não são uma característica da linguagem (como é o caso da especialização) mas são nomes dados para algumas técnicas usadas com templates.
Além de esclarecer algumas dúvidas vou tentar conectar os conceitos com a STL.

Embedded Type Information

Embedded Type Information é a capacidade de um tipo armazenar informações relevantes a si próprio.
Esta capacidade está intimamente ligada aos templates pois podem fornecer a eles a informação de que necessitam para sua instância. Esta informação é colocada em forma de typedefs.

Os containers da STL são exemplos de classes com embedded type information. Pegando o vector como exemplo, encontramos nele os seguintes typedefs:
allocator_type , const_iterator, const_pointer, const_reference, const_reverse_iterator, difference_type, iterator, pointer, reference, reverse_iterator, size_type, value_type.
Este typedefs informam o tipo de iterator, o tipo do allocator, o tipo de dado usado etc, e são utilizados em algorítmos genéricos.
Por exemplo posso perguntar para o container “T”:
Qual é o seu iterator? Qual o tipo de allocator você utiliza?

Uma função template usando a informação do container “T”

template<class T> void printAll(T & container)
{
   T::iterator it = container.begin(); //<--
   For ( ;it != container.end(); ++it)
      cout << *it;
}
  • Percebam que caso a informação do tipo do iterator não estivesse declarada no container eu precisaria de um parâmentro extra na minha função template.


Traits

Algumas vezes a informação contida em um tipo não é suficiente para a implementação de um template. Outras vezes, o tipo não possui e não pode conter nenhuma informação extra, como é o caso dos tipos básicos int, double, etc. Os Traits podem ser usados nestes casos. Traits, é uma pequena classe que traz informações sobre um tipo e/ou informações de como lidar com ele. Podem ser usados em outras classes ou em algorítmos.

Exemplo: Vamos supor que eu queria percorrer um container de ponteiros e deletar todos os items:

template<class T> void DeleteAllItems(T & container)
{
  T::iterator it = container.begin();
  for ( ;it != container.end(); ++it)
  {
    delete *it;
  }
  container.clear();
}

Esta função template funciona perfeitamente para um ponteiro convencional. No entanto eu posso ter o mesmo algorítmo para deletar uma lista de objetos COM. (No COM é usado um método Release da interface IUnknown ao invés do operator delete) Neste caso preciso tratar os diferentes tipos de ponteiros. Esta informação pode ser colocada externamente com a utilização de Traits.
Um Traits que contenha informação de como deletar o ponteiro pode ser definido assim:

// caso genérico
template<class T> void DeleteItemTraits(T p) {
  delete p;
 }

// especializando para o caso do IUnknown
template<> void DeleteItemTraits(IUnknown *p) {
  p->Release(); // caso de um ponteiro COM
}

E o algorítmo genérico pode ser escrito:

template<class T> void DeleteAllItems(T & container)
{
  T::iterator it = container.begin();
  for ( ;it != container.end(); ++it)
  {
    DeleteItemTraits<T::value_type>(*it); //<--
  }
  container.clear();
}

O termo Police também é usado para Traits, para dar a noção de ações sobre o tipo, e não apenas informações. Poderia chamar minha função de DeletePolice por exemplo. A STL possui vários exemplos de Traits. Um destes é a implementação da std::numeric_limits. A numeric_limits possui a função max() que retorna o máximo valor que pode ser contido no tipo.

Exemplo:

cout << "The maximum value for type int is:  "
     << numeric_limits<int>::max( ) //*
     << endl;

Neste caso não era possivel colocar a informação do valor de max dentro do tipo int. Por isso o conceito de traits foi usado. Traits também são usados na STL em classes como a basic_string que precisa de um Traits para tratar o caso do tipo de caractere usado (char ou wchar_t).

  • Não foi comentado no artigo I, mas quando o tipo do template não pode ser deduzido pelo argumento o programador explicitamente informa o tipo com a syntaxe nomefuncao<tipo>, como é o caso da numeric_limits.


Referências

The C++ programming language, Bjarne Stroustrup
C++ Common Knowledge, Stephen C. Dewhurst

Ferramentas pessoais