logo
Contato | Sobre...        
rebarba rebarba

Rodrigo Strauss :: Blog


C++11: auto

A palavra chave auto já existe no C++ desde os tempos mais primórdios. Ela sempre era (sim, era) um modificador de storage de variáveis. Você podia escolher o modificador auto para variáveis de armazenamento automático e register para armazenar a variável em um registrador. Esse modificador hoje em dia é implícito: se não especificado o armazenamento o compilador decide onde armazenar. De forma automática. O modificador register, até onde sei, é grande conhecido do pessoal de embarcados, que muitas vezes trabalham com recursos limitados e precisam gerenciá-los a ponto de dizer ao compilador onde a variável x será armazenada.

Interessante lembrar que a palavra chave register gera alguns efeitos colaterais, como a impossibilidade de pegar o endereço da variável com o operador &. E tem ainda o fato de que muitos compiladores modernos simplesmente não respeitam o register.

  1. //
  2. // Eu dizendo ao compilador que quero a variável a
  3. // *preferencialmente* armazenada em um registrador.
  4. //
  5. register int a = 10;
  6.  
  7. //
  8. // Eu dizendo para o compilador "eis minha variável b, enfie
  9. // onde bem entender"
  10. //
  11. auto int b = 42;
  12.  
  13. //
  14. // Quando não especifico nada, o "enfie onde quiser"
  15. // está subentendido
  16. //
  17. int c = 42;

Com o padrão C++11 o significado dessa palavra chave foi modificada. Agora ela é usada para tipar uma variável de acordo com a expressão a ela atribuída. Na linha "int a = "2 + 2", o compilador sabe que a expressão "2 + 2" retorna um tipo inteiro - na realidade o compilador sempre sabe o tipo de uma expressão em uma linguagem tipada - e pode muito bem acertar a declaração de acordo.

Esse recurso não é lá muito útil para expressões simples e pode até prejudicar a legibilidade do código, já que o leitor terá que efetuar de cabeça a mesma avaliação de expressão que o compilador fez. Ele realmente se torna útil para tipos e subtipos de classes templates, onde só o tamanho da declaração do tipo da variável pode tomar mais da metade da tela. Exemplo:

  1. //
  2. // Variável a será do tipo int
  3. //
  4. auto a = 10;
  5.  
  6. //
  7. // Variável f será do tipo double, devido à regra de promoção de tipos
  8. //
  9. auto f = 10 * 2.0;
  10.  
  11.  
  12. std::map<std::string, std::string> um_mapa;
  13.  
  14. //
  15. // Variável i será do tipo informado
  16. //
  17. std::map<std::string, std::string>::iterator i = um_mapa.begin();
  18.  
  19. //
  20. // Variável biggie_smalls terá o mesmo tipo da variável i,
  21. // mas economizando bastante espaço
  22. //
  23. auto biggie_smalls = um_mapa.begin();
  24.  
  25. //
  26. // Só pra mostrar que essa coisa toda funciona
  27. //
  28. assert(i == biggie_smalls);
  29. i = biggie_smalls;
  30.  
  31. //
  32. // ISSO NÃO FUNCIONA. É preciso haver uma atribuição para
  33. // que o compilador saiba qual o tipo
  34. //
  35. auto x; // <- error 12345: you really don't know how to use auto, do you?

Para quem programa em coisas tipo Haskell isso pode parecer óbvio. Mas já vi trocentas pessoas achando que isso é uma tentativa de transformar C++ em uma linguagem dinâmica. Então, só pra garantir:

A "nova" palavra chave auto NÃO TORNA O C++ UMA LINGUAGEM DINÂMICA. O tipo ainda é estático, você só não precisa informá-lo quando o compilador consegue deduzir isso sozinho.

Ah, isso é equivalente ao var do C# e igual ao funcionamento de declaração de variáveis da linguagem Go. E *não* é igual à declaração de variável em Python, Ruby ou Perl.

Mais um exemplo, e caso específico onde o auto é mais usado:

  1. using std::string;
  2. using std::map;
  3. using std::vector;
  4.  
  5. map<string, vector<string>> connections;
  6.  
  7. //
  8. // Como é hoje
  9. //
  10. for(map<string, vector<string>>::iterator i = connections.begin() ; i != connections.end() ; ++i)
  11. {
  12.  
  13. }
  14.  
  15. //
  16. // Muito mais fácil de ler.
  17. //
  18. for(auto i = connections.begin() ; i != connections.end() ; ++i)
  19. {
  20.  
  21. }

Em 28/11/2011 14:39, por Rodrigo Strauss


  
 
 
Comentários
Valdemar Kjær | em 28/11/2011 | #
Pessoalmente, acho que o novo "auto" dificulta a compreensão imediata do código. Provavelmente usarei pouco - se é que usarei.
Rodrigo Strauss | website | em 28/11/2011 | #
Nem sempre. Coloquei um outro exemplo para mostrar que em casos onde é bem óbvio o tipo, isso facilita a leitura e entendimento do código.
Ari C. Raimundo | em 29/11/2011 | #
Rodrigo,

No mundo do .NET há quem odeie e quem ame o uso do var. Na minha opinião, o uso do var é extremamente produtivo, principalmente quando trabalhamos com LINQ.

Em C++ acredito que o auto também traz facilidades. É óbvio que o retorno do begin é um iterator, não há necessidade de escrever o tipo.

Abraços.

Ari
Wanderley Caloni | website | em 29/11/2011 | #
O exemplo do loop com variável interna do tipo do iterator é a grande sacada, pois economiza na escrita e na leitura do código (o programador já olha direto para o begin sendo atribuído).

Agora, de forma implícita, o que o novo auto possibilita mesmo é seu uso na construção de templates.

[]s
Leandro Del Arco | em 10/12/2014 | #
Lembrando que no C++11 podemos utilizar o range-based for loop:

for(auto i : connections)
{

}

rebarba rebarba
  ::::