Rodrigo Strauss :: Blog
Tutorial de STL, parte 0: Templates
Eu já vinha pensando a bastante tempo em escrever uma série sobre STL, e finalmente resolvi deixar de ser preguiçoso e fazê-lo :-). A STL é um dos recursos mais importantes do C++ - na minha opinião é o mais importante - e mesmo assim muitos programadores não conhecem. Mas como a STL é baseada em templates, vou falar primeiro sobre templates para depois começar a falar da STL.
A funcionalidade de templates (algo como "gabarito" ou "modelo" em português) foi criada basicamente para resolver o problema de funções que precisam tratar diversos tipos de dados. Vamos começar pelo exemplo mais simples possível, uma função que soma dois valores. Precisamos de uma função soma que consiga operar sobre diversos tipos de dados. Usando linguagem C, a solução mais simples possível é:
int soma_int(int x, int y) { return x + y; } float soma_float(float x, float y) { return x + y; } double soma_double(double x, double y) { return x + y; } char soma_char(char x, char y) { return x + y; } /* e assim sucessivamente... Essas funções estão em C, que não suporta sobrecarga de funções (várias funções com mesmo nome, mas com parâmetros diferentes) Isso facilitaria o código que usa as funções (já que todos chamariam a função "soma"), mas não resolveria o problema do código duplicado. */ int main() { soma_int(2,2); soma_float(2.99f, 2.99f); soma_double(5.9999, 6.9999); soma_char('2', '2'); return 0; }
Isso é muito trabalhoso (ok, você pode usar regex e gerar tudo isso, mas...), além do que você precisaria criar novas funções para cada novo tipo de dado. Além disso, uma modificação no algoritmo tornaria a manutenção um inferno para qualquer coisa mais complicada do que a soma de dois valores. O mais importante a se notar nesse trecho de código é que, apesar da declaração das funções serem diferentes, o código é exatamente o mesmo.
Outra possibilidade (leia-se gambiarra) para quem usa C e gosta de fortes emoções, seria fazer uma função de soma que suportasse vários tipo de dados, usando ponteiro void para passar os parâmetros:
#define MAIOR_TIPO_SUPORTADO __int64 void* soma_emocionante(void* px, void* py, char tipo) { static char buffer[sizeof(MAIOR_TIPO_SUPORTADO)]; if(tipo == 'i') { *((int*)buffer) = *((int*)px) + *((int*)py); return buffer; } else if(tipo == 'f') { *((float*)buffer) = *((float*)px) + *((float*)py); return buffer; } else if(tipo == 'd') { *((double*)buffer) = *((double*)px) + *((double*)py); return buffer; } /* se não suportamos o tipo, retornamos NULL */ return NULL; } int main() { int a = 10, b = 20, c; c = *(int*)soma_emocionante(&a, &b, 'i'); }
Eu poderia encher laudas e laudas de texto apontando os defeitos desse código. Não suporta multithread (viu a variável estática?), não resolve o problema do código duplicado (só reduz o número de funções, o que é uma vantagem discutível nesse caso), você só consegue somar variáveis, é fácil de cometer um erro e tomar um GPF, etc, etc. Quanto mais formos tentando resolver esse problema usando os recursos normais do C e do C++ pré padrão ISO (quando os templates não existiam), mais o nosso código vai parecer uma grande gambiarra.
É exatamente esse tipo de problema que os templates resolvem, eles geram todo esse código para você automaticamente, em tempo de compilação, sem remendos, sem void* e sem gambiarras. Vamos ver como ficaria esse código usando templates (agora já em C++ ISO):
template <typename T> T soma(T x, T y) { return x + y; } int main() { soma<int>(2,2); soma<float>(2.99f, 2.99f); soma<double>(5.9999, 6.9999); soma<char>('2', '2'); return 0; }
Quando compilamos esse código, o compilador gera automaticamente o código para a função, substituindo o parâmetro T pelo tipo de dado passado como parâmetro para o template. Isso simplifica bastante o corpo da função, mas não facilita muito o uso, já que só trocamos a sintaxe soma_TIPO para soma<TIPO>. Mas veremos como os templates resolvem esse problema nos próximos posts.
Em 09/05/2006 16:04, por Rodrigo Strauss





Você esqueceu da solução em C via #define:
#define soma(tipo) tipo soma_##tipo (tipo x, tipo y) { return x+y; }
soma(int)
soma(float)
void main(void)
{
soma_int(2,2);
soma_float(2.99f, 2.99f);
}
Oinc, Oinc!