Templates parte 1

De ccppbrasil.org

Tabela de conteúdo

Templates Parte I

Thiago Rosso Adams - 19/03/06

Resolvi escrever um pouco sobre templates, pois apesar de toda biblioteca padrão usar este conceito, muitas pessoas ainda tem uma visão muito superficial sobre este assunto, e deixam de tirar todo o proveito desta maravilhosa característica do C++.

Eu costumo dizer que com templates você faz o compilador trabalhar para você.
Vou colocar um exemplo para servir de motivação e tentar introduzir a nomeclatura de templates.

Motivação

Para iniciar vou usar o exemplo de uma função swap. Esta função faz a troca de valores entre duas variáveis.

void swap(int &a, int &b)
{
  int temp(a);
  a = b;
  b = temp;
}

Mas também preciso da mesma função para double, para string e talvez para outros tipos. Pensando na implementação eu descubro que seria a mesma. Será que o compilador não poderia nos dar uma ajuda ? - Sim e com templates ele pode.
Para isso declaramos uma função template.
Esta função foi chamada de “swap” e possui um parâmetro template chamado “T”.

template<class T>
void swap(T & a, T & b)
{
   T temp(a);
   a = b;
   b = temp;
}

int i1 = 1;
int i2 = 2;
swap(i1, i2); // ok

double d1 = 1.0;
double d2 = 2.0;
swap(d1, d2); // ok

A função swap é instanciada para int e para double pois o compilador pode deduzir o tipo T através do argumento.
De forma similar os templates podem ser usados para declaração de classes.
Por exemplo:

template <class T>
class Array
{
   T * m_pointer;
   [ … ]
   T & at(unsigned int index) const {
     return m_pointer[index];
   }
   [ … ]
}

Neste exemplo a classe array precisa ser instanciada para o tipo T. Ela é chamada de uma classe template.

Array<int> array; // o tipo T precisa ser informado aqui

Este exemplo é encontrado em toda STL com o uso de containers. A vantagem é clara, você não precisa trabalhar de forma polimórfica com os tipos, pois o que você deseja realmente é um array de inteiros.

Até aqui espero que a motivação para o uso esteja clara, e vou lembrar que esta é apenas a “ponta do iceberg”.

Especialização

Uma versão do template para um tipo em especial é chamada especialização. Existem dois tipos de especialização especialização completa ou explícita e a especialização parcial. Para toda especialização primeiramente é preciso um template primário com o caso geral.
Pegando o exemplo da função swap, que será nosso template primário e já foi definido.

Uma especialização para o tipo int seria:

Template<>
void swap(int & a, int & b)
{
  cout << “quando usar int parece esta mensagem”;
  int temp = a;
  a = b;
  b = temp;
}

Este exemplo é simples e talvez não pareça muito útil. Mas eu apenas resisti a tentação de não escrever uma especialização parcial neste momento.

  • A especialização completa é usada para definir uma classe template para um número exato de argumentos.

Especialização Parcial

Vamos seguir adiante.
Especialização parcial é uma especilização em que você necessita de mais parâmetros para concluir a sua especialização. Neste caso a lista de parâmetros não é vazia.
É possível criar uma especialização parcial para o caso de ponteiros por exemplo ou qualquer outro caso.
Voltando ao exemplo da função swap, ela será usada aqui para a especialização da o tipo Array.
Apenas para deixar clara a diferença, uma especialização completa para a classe Array poderia ser escrita assim:

template< >
void swap<Array<int> & a, Array<int> & b>
{
  […]
}

Não parece muito útil certo? Pois esta função funcionaria apenas para o caso de um Array<int>.
Então este é o caso em que a especialização partial é nescessária.

Template<class T>
void swap( Array<T> & a, Array<T> & b)
{
   a.swap(b);
}

em que Array::swap poderia ser definida assim:

void Array<T>::swap(Array<T> & b)
{
   T * tmp = m_pointer;
   m_pointer = b.m_pPointer;   
   b.m_pointer = tmp;
}

Agora a função swap pode ser usada em qualquer Array e a especialização tornou a função muito mais eficiente e segura para o caso da classe Array, em que não será preciso fazer uma cópia.

  • Especialização parcial é uma especilização em que você necessita mais parâmetros para concluir a sua especialização.


Ainda tem mais?

É possível escrever um livro inteiro só sobre templates. Um dos livros mais completos sobre o assunto é "C++ Templates: The Complete Guide" por David Vandevoorde e Nicolai M. Josuttis.

Existem mais conceitos importantes e outros detalhes sobre especialização que não foram expostos. Quando tiver tempo tentarei escrever outras partes nas quais faltaram comentários.

  • Parâmetros default
  • Embedded type information
  • Traits
  • SFINAE
  • typename x class
  • membros template
  • templates de templates
  • Policies
  • algorítmos
  • tratamento de exceções para templates.

Referências:

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

Ferramentas pessoais