logo
Contato | Sobre...        
rebarba rebarba

Rodrigo Strauss :: Blog

follow us in feedly

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


  
 
 
Comentários
Daniel Quadros | website | em 09/05/2006 | #
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!
Rodrigo Strauss | website | em 09/05/2006 | #
Muito bem lembrado. E esse soa bem menos gambiarra do que o meu :-)
Wanderley Caloni Junior | website | e-mail | em 09/05/2006 | #
Ótimo artigo introdutório! Merece inclusive no futuro ser mais uma referência no C++ Wiki =).

Só uma pequena observação, sem querer confundir o leitor iniciante: você realmente quis exibir de modo explícito que estamos passando o tipo na chamada da função soma para clarear os pensamentos e dúvidas dos estudantes ansiosos, e por isso evitou a forma mais direta e, a meu ver, mais bonita da programação genérica em C++? Em código:

soma(2,2);
soma(2.99f, 2.99f);
and so on...

Por último, parabéns pela iniciativa de falar sobre o bê-á-bá da linguagem. Já estava na hora de alguém da comunidade fazer isso (confesso que não me controlo o suficiente para não ir muito longe nas explicações e complicar tudo logo de cara).
Rodrigo Strauss | website | em 10/05/2006 | #
Sim, eu quis deixar isso claro. Tanto que no comentário do código eu coloquei isso:

"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."

Ainda estou pensando se eu crio um "Tutorial de STL, parte 1/2" pra falar desse exemplo e do exemplo dado pelo DQ. :-)
Wanderley Caloni Jr | website | e-mail | em 10/05/2006 | #
Eu estava me referindo ao último exemplo:

soma<int>(2,2);
soma<float>(2.99f, 2.99f);
soma<double>(5.9999, 6.9999);

Onde você tem a opção de deixar a identificação do tipo com o compilador:

soma(2,2); // chama soma<int>
soma(2.99f, 2.99f); // chama soma<float>
soma(5.9999, 6.9999); // chama soma<double>

Rodrigo Strauss | website | em 10/05/2006 | #
Ah tá... :-)

Mas isso vai ficar pra próxima:

"... 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."
Fabio Galuppo | website | e-mail | em 11/05/2006 | #
Inferência e Koenig Lookup devem ser capítulos a parte ;-)

Muito legal esta thread!
Amigo | e-mail | em 24/05/2006 | #
Veja oque te disse nos outros comentarios.

Pra mim, voce é falastrao. Somente copia textos da internet, nada mais.

Aqui, um simples exemplo de como voce programa mal.

Veja só, de alguns exemplos que voce colocou, vieram dois programadores MUITO bons e colocaram exemplos simples aqui, ao inves da gambiarra que voce fez.

Por isso, acho que tem medo de colocar codigos produzidos por voce e verem que realmente programa mal.

Infelizmente, vejo que voce é apenas mais um "metido" a programador brasileiro, nada mais que isso.

Afinal de contas, nem pra exemplos, voce sabe disponibilizar algo bem feito. Veja como os outros programadores mostraram caminhos que nem sequer voce conhecia.

Voce quer se passar como "o bom", "o sabidao", "o melhor", mas na verdade tem muito o que aprender.

Espero realmente ver codigos produzidos por voce nesta pagina e que realmente possa demonstrar que sabe fazer algo bom e nao somente copiar links.
Rodrigo Strauss | website | em 24/05/2006 | #
Isso já está virando spam. Se você colocar comentário em mais um post eu apago TODOS os seus comentários. Considere-se avisado.

Quer discutir volte para http://www.1bit.com.br/content.1bit/weblog/ddknews#1308 e lá a gente conversa.
Amigo | e-mail | em 24/05/2006 | #
Entenda por favor, nao é spam, é que quero demonstrar como voce fala demais e apresenta pouco conteudo.

Como te disse, é uma critica construtiva. Somente isso.
Rodrigo Strauss | website | em 24/05/2006 | #
Tudo bem, então coloque comentário em um post só.
Fabio Galuppo | website | em 24/05/2006 | #
Amigo,

Lamentável sua atitude!

Quem se esconde atrás de um pseudonimo é um covarde, e deve ser ignorado? É algum tipo de inveja?

Pq vc não faz algo construtivo e publica o seu site de conteúdo "avançado"... Ou vc é um nerd espinhudo du caralho?

Cara, o Rodrigo tem a maior boa vontade de publicar conteúdo de diversos tipos, para iniciantes e dummies (como vc)...

Acorda, se quiser falar de desenvolvimento, mostra sua cara, ai pode ser que vc ganhe respeito...
Amigo | em 25/05/2006 | #
Calma Fábio,

É só um pedido para ele publicar algo desenvolvido por ele. Ele sempre "espalha" por ai, que é "o" programador mais foda que existe, digo isto, pois conheco-o pessoalmente (muito pouco) e sei como é a "humildade" do rapaz.

Entao, queria que ele realmente publicasse algo que fosse criado por ele e nao textos copiados de outras paginas e coisas similares.
Rodrigo Strauss | website | em 25/05/2006 | #
Agora tudo ficou muito claro: diz que me conhece, IP da Telemar, sem nada pra fazer...

Game Over pra você amiguinho. Se você está bravinho comigo, você sabe meu telefone do trabalho, me ligue lá se quiser.

Se você quiser espaço para provar as coisas que você diz, como

"Ele sempre "espalha" por ai, que é "o" programador mais foda que existe"

sinta-se a vontade para continuar postando. Mas não espere que eu vá responder, pois ao contrário de você eu tenho mais o que fazer.
Rodrigo Morais | em 04/07/2006 | #
Rodrigo, a atitude desse "Amigo" é realmente lamentável. Concordo com o Fábio ""...pseudonimo é um covarde... É algum tipo de inveja?"" e acho que deve ser inveja sim... lembrando... esse site (e blog) é seu e você escreve o que bem quiser!

Mas concordo com ele no seguinte aspecto: gostaria muito de ver artigos (no estilo do www.codeproject.com) nesse site pois é dessa forma que melhor aprendo a programar c++ (programo só por diversão e ainda estou estudando STL e wxWidgets).

Sem me alongar mais, veja o site http://walaber.com/ . A atitude desse cara de liberar o código fonte de um aplicativo como o Stunt Playground foi muito louvável.
Rodrigo Strauss | website | em 04/07/2006 | #
Rodrigo, a grande diferença é que o Code Project é um site feito por centenas de pessoas, e esse é um site pessoal. Acho que o Code Project é muito bom no que faz, qualquer tentativa minha de seguir o estilo deles não funcionaria, eu sou um só.

Eu acho que escrever tutoriais é uma boa forma de ajudar, e é mais o meu perfil do que fazer programas inteiros e simplesmente disponibilizar os fontes.

Sobre o Stunt Playground, não esqueça que o pessoal da ID Software também teve a atitude louvável de liberar os fontes do Quake, além do SourceForge estar cheio de pessoas com a mesma atitude :-)
claudio Peralta Ribeiro | em 07/09/2006 | #
Gostaria de saber aonde eu encontro uma referencia realmente completa do C++ ,mas realmente completa mesmo,pois tenho achado sempre apenas uma parte
Rodrigo Strauss | website | em 08/09/2006 | #
Dê uma olhada em www.1bit.com.br/content.1bit/weblog/faq_cpp_start
Vinícius Godoy | website | em 26/12/2007 | #
Olá Rodrigo. Demorei a ver o seu site, mas agora que vi, vou adicionar aos meus favoritos.

Também sou programador C++ e Java. E também nasci em 80.
Como não são tão ligado a tecnologias MS (mas também não tenho nada contra elas), espero aprender bastante aqui. ;)

Esses textos sobre STL são excelentes. Gostei muito de você começar revisando o assunto de templates. É um tema que muitos vêem, mas poucos tem coragem de falar sobre ele. Parabéns!
Bruno | em 27/08/2008 | #
Ao invéz de vir aqui criticar pq esse filhos da puta não passam a frequentar outros sites?

Ou melhor... pq não criam um site melhor, para tds possam ver o qto são bons..


aff..
Felipe Ferreri Tonello | website | em 02/12/2008 | #
Muito bom seus artigos Rodrigo, espero que você continue a escrever mais. Estou usando eles como base de meus estudos de C++.

por falar em macros como o Daniel comentou. Eu acredito que teria uma maneira mais eficaz de se fazer a soma genérica em C:

#define soma(a,b,tipo) (tipo)a + (tipo)b

void main(void)
{
float a;
int b;
a = soma(21.2,10.1, float);
b = soma(11,10, int);
}

Abraços
Eder Renato | e-mail | em 04/05/2011 | #
Para quem está aprendendo toda ajuda é bem vinda, por favor, quanto mais críticas, mais soluções. Todos estão de parabéns.
Mas lembrando que crítica construtiva é aquela que antes de mais nada apresenta soluções viáveis para o propósito.
Ze Eduardo | em 17/05/2011 | #
Show de bola esse blog, que pena que fui conhecer só agora.
Gostei também das soluções em C usando macros.
Bruno Sugai | e-mail | em 01/09/2011 | #
Estou iniciando meu aprendizado na linguagem c++ e vou te contar, voce acabou de se tornar meu professor, otimo post.
Qualque dia lhe mando uma fruta pelo correio, valew.
Hudson Alves | e-mail | em 23/06/2016 | #
Parabéns pelo artigo, muito bom mesmo.
Algo a dizer?
Nome:


Site:


E-mail:


Escreva o número vinte e seis:


 Não mostre meu e-mail no site, não serve pra nada mesmo...

Comentário





Os comentários devem ser sobre assuntos relativos ao post, eu provavelmente apagarei comentários totalmente offtopic. Se quiser me enviar uma mensagem, use o formulário de contato. E não esqueça: isso é um site pessoal e eu me reservo o direito de apagar qualquer comentário ofensivo ou inapropriado.
rebarba rebarba
  ::::