Você está na página 1de 13

Templates em C++

Templates permitem a criao de cdigo-fonte mais limpo e com menos esforo por parte do programador. Isso se d atravs de funes e classes genricas, que sero
discutidas nesta breve introduo sobre templates em C++.

ndice
1.

Template de funes

2. Template de classes
2.1 Exemplo de template de classe: Stack
2.2 Exemplo de template de classe: Pair
3.

Especializao de templates de funes e de templates de classes

4. Templates e herana
4.1 Template que herda de outro template
4.2 Template que herda de uma classe "normal" (no-template)
4.3 Classe "normal" (no template) que herda de um template
5.

Bibliografia

1. Templates de funes
Iremos motivar a discusso sobre templates de funes a partir de um exemplo. Suponha que temos um programa que imprime um array de inteiros qualquer, ordena esse
array e o imprime novamente. Esse programa possue duas funes: print_array() e insertion_sort().

#include <iostream>
void print_array(int arr[], int length)
{
for (int i = 0; i < length; i++)
{
std::cout << arr[i] << " ";
}
std::cout << "\n";
}
void insertion_sort(int arr[], int length)
{
int i, j, key;
for(i = 1; i < length; i++)
{
key = arr[i];
j = i - 1;
while(j >= 0 && arr[j] > key)
{
arr[j+1] = arr[j];
j -= 1;
}
arr[j+1] = key;
}
}
int main()
{
int a[10] = {3, 20, -1 , 4, 2, 50, 10, 9, 8, 13};
std::cout << "Array original: ";
print_array(a,10);
std::cout << "Array ordenado: ";
insertion_sort(a,10);
print_array(a,10);
}

O cdigo acima funciona. Contundo, e se alm do array de inteiros quisermos realizar o mesmo procedimento para um array de double? Uma soluo seria utilizar
sobrecarga de funes ("function overloading").

#include <iostream>
void print_array(int arr[], int length)
{
for (int i = 0; i < length; i++)
{
std::cout << arr[i] << " ";
}
std::cout << "\n";
}
void print_array(double arr[], int length)
{
for (int i = 0; i < length; i++)
{
std::cout << arr[i] << " ";
}
std::cout << "\n";
}

void insertion_sort(int arr[], int length)


{
int i, j, key;
for(i = 1; i < length; i++)
{
key = arr[i];
j = i - 1;
while(j >= 0 && arr[j] > key)
{
arr[j+1] = arr[j];
j -= 1;
}
arr[j+1] = key;
}
}
void insertion_sort(double arr[], int length)
{
int i, j;
double key;
for(i = 1; i < length; i++)
{
key = arr[i];
j = i - 1;
while(j >= 0 && arr[j] > key)
{
arr[j+1] = arr[j];
j -= 1;
}
arr[j+1] = key;
}
}
int main()
{
int a[10] = {3, 20, -1 , 4, 2, 50, 10, 9, 8, 13};
std::cout << "Array original: ";
print_array(a,10);
std::cout << "Array ordenado: ";
insertion_sort(a,10);
print_array(a,10);
double d[10] = {1.01, -0.3, 3.14, 1.2, 20.5, 1.3, 7.7, 0.1, 9.2, -3.0};
std::cout << "Array original: ";
print_array(d,10);
std::cout << "Array ordenado: ";
insertion_sort(d,10);
print_array(d,10);
}

Novamente o cdigo est correto. No entanto, o nmero de linhas aumentou substancialmente. E se quisermos colocar mais um array no programa, s que de string?
Embora os procedimentos de imprimir e ordenar sejam praticamente idnticos, precisamos repeti-los sempre, tantas vezes quanto tipos de array diferentes desejarmos.
Isso ruim pois:

No temos pacincia nem tempo para ficar copiando, colando e alterando cdigo.

A repetio excessiva de funes quase idnticas atrapalha a leitura, deixando cdigo "sujo" e pouco elegante.

A manuteno dificultada. Caso seja necessrio realizar alguma alterao no procedimento (e.g. usar quicksort ao invs de insertion sort ou imprimir um
elemento do array por linha ao invs de todos juntos) deveremos modificar diversas funes. A probabilidade de errarmos no processo muito maior do que se
tivessemos apenas uma funo.

Para evitarmos esse cenrio incmodo podemos usar um template de funo. Ele nos permite criar uma funo genrica, uma abstrao capaz de lidar com qualquer
tipo de parmetro, seja um tipo bsico do C++ ou um tipo definido pelo programador.
Em termos gerais a sintaxe para declarao de um template de funo :

template <typename T>


void foobar(T arg1, T arg2);

A definio dessa funo foobar() seria:

template <typename T>


void foobar(T arg1, T arg2)
{
// Cdigo relevante aqui, como de praxe.
// A nica diferena que voc pode tratar T como se fosse um tipo.
}

No se preocupe, com alguns exemplos concretos tudo vai ficar mais tangvel.

Suponha que precisamos de uma funo minimum() que recebe dois argumentos do mesmo tipo e retorna o menor deles. Se queremos usar minimum() com variveis
int, double, char, e at mesmo objetos std::string, podemos escrever um template funo para um tipo genrico T.

Cdigo-fonte
#include <iostream>
#include <string>
template <typename T>
inline const T& minimum(const T& arg1, const T& arg2)
{
return (arg1 < arg2) ? arg1 : arg2;
}

Sada do programa
1
2.71
B
Finland

// Fazemos a funo inline e passamos os argumentos


// por referncia por questo de desempenho.
int main()
{
std::cout << minimum(1,10) << std::endl;
std::cout << minimum(2.71,3.14) << std::endl;
std::cout << minimum('X','B') << std::endl;
std::string s1("Finland"), s2("France");
std::cout << minimum(s1, s2) << std::endl;
}

Mais prtico do que escrever quatro funes, uma para cada tipo, no ? Esse trabalho fica para o compilador. Ao encontrar uma chamada como minimum(1,10) o
compilador substitui o tipo genrico T por int e gera uma funo int minimum(int arg1, int arg2). Da mesma forma, ao se deparar com minimum(2.71, 3.14)
o compilador troca T por double e gera uma funo double minimum(double arg1, double arg2). O processo idntico para char e assim por diante.
Voltando e arrumando o programa inicial que lida com impresso e ordenao de arrays, criamos templates para print_array() e insertion_sort(). Adicionamos
tambm um array de string para reforar a versatilidade do template de funo com qualquer tipo.

#include <iostream>
#include <string>
template <typename T>
void print_array(T arr[], int length)
{
for (int i = 0; i < length; i++)
{
std::cout << arr[i] << " ";
}
std::cout << "\n";
}
template <typename T>
void insertion_sort(T arr[], int length)
{
int i, j;
T key;
for(i = 1; i < length; i++)
{
key = arr[i];
j = i - 1;
while(j >= 0 && arr[j] > key)
{
arr[j+1] = arr[j];
j -= 1;
}
arr[j+1] = key;
}
}
int main()
{
int a[10] = {3, 20, -1 , 4, 2, 50, 10, 9, 8, 13};
std::cout << "Array original: ";
print_array(a,10);
std::cout << "Array ordenado: ";
insertion_sort(a,10);
print_array(a,10);
double d[10] = {1.01, -0.3, 3.14, 1.2, 20.5, 1.3, 7.7, 0.1, 9.2, -3.0};
std::cout << "Array original: ";
print_array(d,10);
std::cout << "Array ordenado: ";
insertion_sort(d,10);
print_array(d,10);
std::string s[5] = {"Ocelot", "Tiger", "Cat", "Lion", "Leopard"};
std::cout << "Array original: ";
print_array(s,5);
std::cout << "Array ordenado: ";
insertion_sort(s,5);
print_array(s,5);
}

Como esperado, temos a sada:

Array
Array
Array
Array
Array
Array

original:
ordenado:
original:
ordenado:
original:
ordenado:

3 20 -1 4 2 50 10 9 8 13
-1 2 3 4 8 9 10 13 20 50
1.01 -0.3 3.14 1.2 20.5 1.3 7.7 0.1 9.2 -3
-3 -0.3 0.1 1.01 1.2 1.3 3.14 7.7 9.2 20.5
Ocelot Tiger Cat Lion Leopard
Cat Leopard Lion Ocelot Tiger

Para finalizar a apresentao de templates de funes, algumas observaes gerais importantes.


provvel que voc encontre em outros lugares uma sintaxe diferente da apresentada aqui. Ao invs de

template <typename T>


// ...

vrios programadores utilizam

template <class T>


// ...

Para o C++ no existe nenhuma diferena semanticamente, isto , os dois significam a mesma coisa. Sendo assim a questo se reduz a gosto pessoal. O importante
tentar manter uma padro consistente.
Ainda sobre sintaxe, observe que nos exemplos utilizamos a letra T como "type parameter". Isso um costume entre a maioria dos programadores, e no uma imposio
da linguagem! Voc pode usar o identificador que quiser, como tipo, type, a, b, X, Y, desde que ele obedea as regras de nomeao de varives do C++.
Por fim, ateno! Anteriormente dissemos que templates de funes podem ser utilizados para qualquer tipo. Na verdade, existem restries. Com tipos bsicos do C++
no preciso preocupar-se, mas para tipos definidos pelo usurio necessrio que todas as operaes utilizadas no template faam sentido. Por exemplo, imagine que
criamos uma classe chamada Triangle que guarda os comprimentos dos lados de um tringulo e possue mtodos para o clculo do permetro e da rea. Se quisermos
usar um array de objetos Triangle com o template de funo print_array(), definido nos exemplos anteriores, devemos fazer a sobrecarga do operador << para
std::ostream.

#include <iostream>
#include <cmath>
class Triangle
{
public:
Triangle(double a, double b, double c) : s1(a), s2(b), s3(c) {}
double area() const {return sqrt((perimeter()/2)*(perimeter()/2 - s1)*(perimeter()/2 - s2)*(perimeter()/2 - s3));}
double perimeter() const {return s1 + s2 + s3;}
friend std::ostream& operator<< (std::ostream &out, Triangle &some_triangle);
private:
double s1, s2, s3;
};
// Fazendo a sobrecarga do operador de insero <<
std::ostream& operator<< (std::ostream &output, Triangle &some_triangle)
{
output << "Sides: "
<< some_triangle.s1 << " "
<< some_triangle.s2 << " "
<< some_triangle.s3 << "\n"
<< "Perimeter: " << some_triangle.perimeter() << "\n"
<< "Area: "
<< some_triangle.area() << "\n";
return output;
}
template <typename T>
void print_array(T arr[], int length)
{
for(int i = 0; i < length; i++)
{
std::cout << arr[i] << "\n";
}
}
int main()
{
Triangle triangles_array[3] = {Triangle(14.0, 16.0, 20.0), Triangle(7.1, 8.9, 10.5), Triangle(1.1, 2.4, 3.1)};
print_array(triangles_array, 3);
};

2. Templates de classes
Com templates de classes podemos criar classes genricas que funcionam para qualquer tipo de dado. Assim como para templates de funes, temos as vantagens do
reaproveitamento do cdigo de maneira inteligente e da manuteno facilitada.
A definio de um template de classe praticamente idntica a definio de um template de funo.

template <typename T>


class MyClass
{
// Cdigo relevante aqui, como de praxe.
// A nica diferena que voc pode tratar T como se fosse um tipo qualquer.
}

Nas subsees seguintes damos alguns exemplos prticos de templates de classes.

2.1 Exemplo de template de classe: Stack


A stack, conhecida tambm como "pilha", uma estrutura de dados simples e muito comum tanto na computao como na vida cotidiana. Stacks so caracterizadas pelo
acrnimo LIFO (Last In, First Out), que em portugus significa "ltimo a entrar, primeiro a sair". Em uma stack, os elementos so sempre inseridos no topo, atravs da
operao push. Considere, por exemplo, a criao de uma stack de livros. Uma sequncia de operaes push pode ser algo como:

Por sua vez, a remoo de elementos tambm acontece somente no topo, atravs da operao pop. Uma sequncia de operaes pop pode ser algo como:

Um template de classe lhe permite escrever um cdigo genrico que serve para criar pilhas de int, double, char, string, roupas, pratos, livros,
#ifndef STACKTEMPLATE_H
#define STACKTEMPLATE_H
#include <cstdlib>
#include <vector>
// Nesse exemplo utilizamos o container vector da STL (Standard Template Library) para facilitar a implementao.
template <typename T>
class Stack
{
public:
void push(T new_element)
{
elements.push_back(new_element);
}
T peek() const
{
if(!elements.empty())
{
T last = elements.back();
return last;
}
else
{
exit(1);
}
}
void pop()
{
if(!elements.empty())
{
elements.pop_back();
}
}
bool is_empty() const {return elements.empty();}
int get_size() const {return elements.size();}
private:
std::vector<T> elements;
};

Simples, no mesmo? Como nos templates de funes, basta escrever T onde voc quiser generalizar o tipo de dado. Depois de terminado o template, voc pode criar
objetos como a seguir:
Stack<int> istack; // Cria uma pilha de int chamada istack
Stack<double> dstack; // Cria uma pilha de double chamada dstack
Stack<char> cstack; // Cria uma pilha de char chamada cstack
Stack<std::string> sstack; // Cria uma pilha de string chamada sstack
// E assim por diante...

Observe que necessrio especificar claramente o tipo de dado na declarao do objeto.


Stack pilha_de_float; // ERRADO!
Stack<float> pilha_de_float; // CORRETO!

Abaixo temos um exemplo de utilizao da classe Stack para uma pilha de inteiros:
Cdigo-fonte
#include "stacktemplate.h"
#include <iostream>
int main()
{
Stack<int> istack;

Sada do programa
istack size before pushing: 0
Pushing: 1 2 4 8
istack size after pushing: 4
Popping: 8 4 2 1
istack size after popping: 0

std::cout << "istack size before pushing: " << istack.get_size() << "\n";
std::cout << "Pushing: ";
for(int i = 1; i < 10; i *= 2)
{
std::cout << i << " ";
istack.push(i);
}
std::cout << "\nistack size after pushing: " << istack.get_size() << "\n";
std::cout << "Popping: ";
while(!(istack.is_empty()))
{
std::cout << istack.peek() << " ";
istack.pop();
}
std::cout << "\nistack size after popping: " << istack.get_size() << "\n";
}

Para o exemplo inicial de uma pilha de livros, suponha que voc possue uma classe Book como esta:
#ifndef BOOKS_H
#define BOOKS_H
#include <iostream>
#include <iomanip>
#include <iostream>
class Book
{
public:
Book(std::string n_isbn, std::string n_author, std::string n_title, int n_edition, int n_year) :
isbn(n_isbn), author(n_author), title(n_title), edition(n_edition), year(n_year) {}
std::string get_isbn() const {return isbn;}
std::string get_author() const {return author;}
std::string get_title() const {return title;}
int get_edition() const {return edition;}
int get_year() const {return year;}
friend std::ostream& operator<< (std::ostream &out, const Book &some_book);
private:
std::string isbn;
std::string author;
std::string title;
int edition;
int year;
};
std::ostream& operator<< (std::ostream &out, const Book &some_book)
{
out << '\n';
out << "ISBN: " << std::setw(35) << std::setfill('.') << some_book.get_isbn() << '\n';
out << "Author: " << std::setw(33) << std::setfill('.') << some_book.get_author() << '\n';
out << "Title: " << std::setw(34) << std::setfill('.') << some_book.get_title() << '\n';
out << "Edition: " << std::setw(32) << std::setfill('.') << some_book.get_edition() << '\n';
out << "Year: " << std::setw(35) << std::setfill('.') << some_book.get_year() << '\n';
out << '\n';
return out;
}
#endif // BOOKS_H

Voc tambm pode utilizar o template de classe Stack para criar uma pilha de objetos da classe Book.
Cdigo-fonte
#include "stacktemplate.h"
#include "books.h"
#include <iostream>
int main()
{
Book b1("1-59059-472-X",
Book b2("0-201-63182-X",
Book b3("0-17-771074-8",
Book b4("0-13-979809-9",
Book b5("2-8315-1367-7",

Sada do programa
Current stack size: 5
Removing top!

"Grant Palmer", "Physics for Game Programmers", 1, 2005);


"Guido Buzzi-Ferraris", "Scientific C++", 1, 1993);
"Anthony Philip French", "Newtonian Mechanics", 1, 1971);
"Bruce Eckel", "Thinking In C++: Volume One", 2, 2000);
"Eric Bailey, Ruth Balley", "Discover Canada", 1, 1995);

Stack<Book> books_stack;
books_stack.push(b1);
books_stack.push(b2);
books_stack.push(b3);
books_stack.push(b4);
books_stack.push(b5);
while(!(books_stack.is_empty()))
{
std::cout << "Current stack size: " << books_stack.get_size() << '\n';
std::cout << "Removing top!" << '\n';
std::cout << books_stack.peek();
books_stack.pop();
}
}

ISBN: ......................2-8315-1367-7
Author: .........Eric Bailey, Ruth Balley
Title: ...................Discover Canada
Edition: ...............................1
Year: ...............................1995
Current stack size: 4
Removing top!
ISBN: ......................0-13-979809-9
Author: ......................Bruce Eckel
Title: .......Thinking In C++: Volume One
Edition: ...............................2
Year: ...............................2000
Current stack size: 3
Removing top!
ISBN: ......................0-17-771074-8
Author: ............Anthony Philip French
Title: ...............Newtonian Mechanics
Edition: ...............................1
Year: ...............................1971
Current stack size: 2
Removing top!
ISBN: ......................0-201-63182-X
Author: .............Guido Buzzi-Ferraris
Title: ....................Scientific C++
Edition: ...............................1
Year: ...............................1993
Current stack size: 1
Removing top!
ISBN: ......................1-59059-472-X
Author: .....................Grant Palmer
Title: ......Physics for Game Programmers
Edition: ...............................1
Year: ...............................2005

2.2 Exemplo de template de classe: Pair


No livro Absolute C++, 5th Edition, de W. J. Savitch, o exerccio 7 do captulo 16 (p. 729) pede a criao de um template de classe chamado Pair, que recebe um par de
variveis de tipos distintos ou no (e.g. um par int e double, char e float, bool e string).
Esse um caso que vale a pena notar pois na maioria dos exemplos mais simples utilizamos apenas um "type parameter", como T. Porm nada impede que voc crie
templates de classes com mltiplos "type parameters" como T1, T2, T3, T4, ...
Abaixo segue a resoluo do exerccio.

template <typename T1, typename T2>


class Pair
{
public:
Pair(T1 first_value, T2 second_value) : first(first_value), second(second_value) {}
void set_first(T1 new_value) {first = new_value;}
void set_second(T2 new_value) {second = new_value;}
T1 get_first() const {return first;}
T2 get_second() const {return second;}
private:
T1 first;
T2 second;
};

Com o template de classe acima voc pode instanciar objetos como:

Pair<int, double> idpair(7, 3.14); // par de inteiro e double


Pair<char, float> cfpair('A', 1.0); // par de char e float

3. Especializao de templates
Templates permitem uma definio genrica de uma funo ou classe que serve para qualquer tipo de dado. No entanto, pode ser que voc deseje um comportamento
diferente para um tipo de dado especfico. Por exemplo, suponha que voc possue um template de funo como este:
template <typename T>
void print_variable(const T &variable)
{
std::cout << variable << std::endl;
}

Utilizando esse template com o cdigo abaixo:


int main()
{
int integer = 10;
print_variable(integer);
char character = 'A';
print_variable(character);
bool boolean = true;
print_variable(boolean);
double edouble = 2.718281828;
print_variable(edouble);
}

Temos a seguinte sada:


10
A
1
2.71828

E se voc quiser que as variveis bool sejam impressas com as palavras true ou false ao invs de 1 e 0? Alm disso, e se for necessrio uma preciso maior com o tipo
double? Voc pode criar especializaes do template de funo print_variable!
template <>
void print_variable<bool>(const bool &variable)
{
std::cout << std::boolalpha << variable << std::endl;
}
template <>
void print_variable<double>(const double &variable)
{
std::cout << std::setprecision(10) << variable << std::endl;
}

Essa modalidade de especializao chamada de especializao completa. Observe que deixamos a lista de parmetros do template vazia (template <>) pois fixamos
o tipo do argumento na especializao. Fazendo isso, o programa final fica como abaixo.

#include <iostream>
#include <iomanip>
template <typename T>
void print_variable(const T &variable)
{
std::cout << variable << std::endl;
}
template <>
void print_variable<bool>(const bool &variable)
{
std::cout << std::boolalpha << variable << std::endl;
}
template <>
void print_variable<double>(const double &variable)
{
std::cout << std::setprecision(10) << variable << std::endl;
}
int main()
{
int integer = 10;
print_variable(integer);
char character = 'A';
print_variable(character);
bool boolean = true;
print_variable(boolean);
double edouble = 2.718281828;
print_variable(edouble);
}

Agora temos o resultado desejado.


10
A
true
2.718281828

Existe ainda uma outra modalidade de especializao, chamada de especializao parcial. Retomando o template de classe Pair da seo 2.2 percebemos que apesar de
podermos criar pares de ponteiros eles no iro se comportar de forma adequada.

#include <iostream>
template <typename T1, typename T2>
class Pair
{
public:
Pair(T1 first_value, T2 second_value) : first(first_value), second(second_value) {}
void set_first(T1 new_value) {first = new_value;}
void set_second(T2 new_value) {second = new_value;}
T1 get_first() const {return first;}
T2 get_second() const {return second;}
private:
T1 first;
T2 second;
};
int main()
{
int *iptr = new int(42);
double *dptr = new double(3.14);
std::cout << "Printing iptr e dptr:\n";
std::cout << *iptr << " " << *dptr << "\n";
Pair<int*,double*> ptr_pair(iptr,dptr);
std::cout << "Printing ptr_pair:\n";
std::cout << *(ptr_pair.get_first()) << " " << *(ptr_pair.get_second()) << "\n";
delete iptr;
delete dptr;
std::cout << "Printing ptr_pair again:\n";
std::cout << *(ptr_pair.get_first()) << " " << *(ptr_pair.get_second()) << "\n";
}

Se voc domina as regras bsicas de ponteiros fcil reconhecer o problema. No exemplo, o construtor de Pair<int*,double*> inicializa o membro first apontando
para o mesmo endereo de iptr e o membro second apontando para o mesmo endereo de dptr. Isso claramente problemtico.

Dar delete nos ponteiros iptr e dptr significa invalidar os membros da instncia ptr_pair de Pair<int*, double*> criada anteriormente!
A sada do cdigo anterior demonstra isso.

Printing iptr e dptr:


42 3.14
Printing ptr_pair:
42 3.14
Printing ptr_pair again:
0 1.11202e-316

// Acessando ponteiros quebrados!

Uma soluo nesse caso seria duplicar as variveis apontadas por iptr e dptr de forma que ptr_pair.first e ptr_pair.second possuam suas prprias cpias. Ou
seja, queremos algo como no diagrama abaixo:

Podemos criar uma especializao do template de classe Pair que funciona corretamente com ponteiros. A especializao dita parcial pois funciona para todos tipos
de ponteiros (e.g., int*, double*, char*, ...), ou seja, ainda preserva alguma generalidade, ao contrrio de uma especializao completa que restringe explicitamente o
tipo de dado.

#include <iostream>
template <typename T1, typename T2>
class Pair
{
public:
Pair(T1 first_value, T2 second_value) : first(first_value), second(second_value) {}
void set_first(T1 new_value) {first = new_value;}
void set_second(T2 new_value) {second = new_value;}
T1 get_first() const {return first;}
T2 get_second() const {return second;}
private:
T1 first;
T2 second;
};
// ---------------------------------// | especializao parcial abaixo! |
// ---------------------------------template <typename T1, typename T2>
class Pair<T1*, T2*>
{
public:
Pair (T1 *first_value, T2 *second_value)
: first(new T1(*first_value)), second(new T2(*second_value))
{
}
~Pair()
{
delete first;
delete second;
}
void set_first(T1 *new_value) {first = new T1(*new_value);}
void set_second(T2 *new_value) {second = new T2(*new_value);}
T1* get_first() const {return (new T1(*first));}
T2* get_second() const {return (new T2(*second));}
private:
T1 *first;
T2 *second;
};
int main()
{
int *iptr = new int(42);
double *dptr = new double(3.14);
std::cout << "Printing iptr e dptr:\n";
std::cout << *iptr << " " << *dptr << "\n";
Pair<int*,double*> ptr_pair(iptr,dptr);
std::cout << "Printing ptr_pair:\n";
std::cout << *(ptr_pair.get_first()) << " " << *(ptr_pair.get_second()) << "\n";
delete iptr;
delete dptr;
std::cout << "Printing ptr_pair again:\n";
std::cout << *(ptr_pair.get_first()) << " " << *(ptr_pair.get_second()) << "\n";
}

A sada agora o que espervamos.

Printing iptr e dptr:


42 3.14
Printing ptr_pair:
42 3.14
Printing ptr_pair again:
42 3.14

Concluindo o tpico de especializaes de templates, vamos recapitular os dois tipos de especializaes.


Uma especializao completa fixa todos os parmetros do template.
Exemplo 1
// Template primrio
template <typename T>
void foo_function(T &some_variable)
{
// Cdigo para tipo genrico T...
}
// Especializao completa
template <>
void foo_function<int>(int &some_variable)
{
// Implementao especfica para int...
}

Exemplo 2
// Template primrio
template <typename T1, typename T2, typename T3>
class bar_class
{
// Cdigo para tipos genricos T1, T2, T3...
};
// Especializao completa
template <>
class bar_class<char, double, int>
{
// Implementao especfica quando os trs parmetros so char, double, int...
};

Uma especializao parcial fixa somente alguns dos parmetros, preservando ainda algum grau de generalidade.

Exemplo 1
// Template primrio
template <typename T1, typename T2, typename T3>
class bar_class
{
// Cdigo para tipos genricos T1, T2, T3...
};
// Especializao parcial
template <typename T1, typename T2>
class bar_class<T1, T2, float>
{
// Implementao especfica quando o ltimo parmetro float...
};

O exemplo abaixo tambm uma especializao parcial. Observe que a especializao fixa todos os parmetros do template como ponteiros, mas ainda existe uma
generalidade pois so permitidos ponteiros para qualquer tipo. Se todos os parmetros fossem fixados como ponteiros para int, por exemplo, a sim teramos uma
especializao completa.

Exemplo 2
// Template primrio
template <typename T1, typename T2, typename T3>
class bar_class
{
// Cdigo para tipos genricos T1, T2, T3...
};
// Especializao parcial
template <typename T1, typename T2, typename T3>
class bar_class<T1*, T2*, T3*>
{
// Implementao especfica quando todos parmetros so ponteiros para tipos genricos...
};

4. Templates e herana
Muitos livros e artigos ignoram ou falam muito brevemente sobre herana e templates de classes. No h nada de muito novo ou terrivelmente complicado se comparado
com uso de herana que voc j conhece, sem templates. Iremos expor apenas alguns exemplos ilustrativos para voc se acostumar com a sintaxe dos templates.

4.1 Template que herda de outro template


Definimos nas sees anteriores o template de classe Pair que nos permite criar pares de variveis de qualquer tipo. Se alm de pares quisermos trios de variveis,
podemos criar um template de classe chamado Triplet que herda de Pair.
Cdigo-fonte
#include <iostream>
template <typename T1, typename T2>
class Pair
{
public:
Pair(T1 first_value, T2 second_value) : first(first_value), second(second_value) {}
void set_first(T1 new_value) {first = new_value;}
void set_second(T2 new_value) {second = new_value;}
T1 get_first() const {return first;}
T2 get_second() const {return second;}
private:
T1 first;
T2 second;
};

Sada do programa
Printing pair:
7 4.815
Printing triplet:
C 1 0.51

template <typename T1, typename T2, typename T3>


class Triplet : public Pair<T1, T2>
{
public:
Triplet(T1 first_value, T2 second_value, T3 third_value) : Pair<T1,T2>(first_value,
second_value), third(third_value) {}
void set_third(T3 new_value) {third = new_value;}
T3 get_third() const {return third;}
private:
T3 third;
};
int main()
{
Pair<int,double> duo(7, 4.815);
std::cout << "Printing pair:\n";
std::cout << duo.get_first() << " " << duo.get_second() << "\n";
Triplet<char,bool,float> trio('C', true, 0.51);
std::cout << "Printing triplet:\n";
std::cout << trio.get_first() << " " << trio.get_second() << " " << trio.get_third() << "\n";
}

4.2 Template que herda de uma classe "normal" (no-template)


Nesse exemplo no fornecemos a implementao das classes, pois queremos compactar o cdigo mostrando apenas as declaraes.

class Item
{
// Classe "comum" (no template) que descreve caractersticas bsicas
// de um item qualquer como massa, dimenses, valor, ...
// Pode ser a classe base de qualquer coisa material.
};
template <typename T>
class Pote : public Item
{
// Template de classe para potes que herda de uma classe "normal" (no template).
// possvel instaciar um pote de qualquer tipo.
};

4.3 Classe "normal" (no template) que herda de um template


Repetimos parte do exemplo anterior para maior clareza. Agora derivamos a classe Baleiro do template Pote.

class Item
{
// Classe "comum" (no template) que descreve caractersticas bsicas de um item qualquer como massa, dimenses, valor, ...
// Pode ser a classe base de qualquer coisa material.
};
template <typename T>
class Pote : public Item
{
// Template de classe para potes que herda de uma classe "normal" (no template).
// possvel instaciar um pote de qualquer tipo.
};
class Balas
{
// Classe "comum" (no template) que descreve guloseimas.
};
class Baleiro : public Pote<Balas>
{
// Classe "comum" (no template) que herda de um template.
// Um baleiro um pote de balas. A classe descreve esse tipo especial de pote.
};

5. Bibliografia
STROUSTRUP, Bjarne. The C++ Programming Language. 3 ed. Westford, Massachusetts: Addison Wesley, 2007.
SAVITCH, Walter. Absolute C++. 5 ed. Upper Saddle River, New Jersey: Addison-Wesley, 2012.
SHTERN, Victor. Core C++: A Software Engineering Approach. 1 ed. Upper Saddle River, New Jersey: Prentice-Hall, 2000.
Templates and Template Classes in C++. In: Cprogramming.com. Disponvel em < http://www.cprogramming.com/tutorial/templates.html > Acesso em: 12 jun 2013.
An Idiot's Guide to C++ Templates - Part 1. In: CodeProject. Disponvel em < http://www.codeproject.com/Articles/257589/An-Idiots-Guide-to-Cplusplus-Templates-Part-1 > Acesso em: 12 jun 2013.
Class templates (C++ only). In: IBM AIX Compiler Information Center. Disponvel em < https://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp > Acesso em: 12 jun 2013.

Autor: Dr. Hank Frum Hardware.com.br


Data: 18 de junho de 2013

Você também pode gostar