Devo usar o novo recurso 'automático' do C ++ 11, especialmente em loops?

20

Quais são os prós / contras de usar a autopalavra - chave, especialmente em loops?

for(std::vector<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->something();
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->second->something();
}

for(auto it = x.begin(); it != x.end(); it++ )
{
   it->??
}

Parece que se você não sabe se você tem um iterador para um mapa ou um vector que você não saberia se ao uso firstou secondou apenas diretamente acessar propriedades do objeto, não?

Isso me lembra o debate em C # sobre a utilização da palavra-chave var. A impressão que estou chegando até agora é que, no mundo C ++, as pessoas estão prontas para adotar a autopalavra-chave com menos esforço do que varno mundo C #. Para mim, meu primeiro instinto é que gosto de saber o tipo da variável para saber quais operações posso executar nela.

Do utilizador
fonte
3
Esperar! Houve uma briga sobre se usar var? Eu senti falta disso.
pdr 12/05
21
Mesmo melhor, você pode apenas usar for (auto& it : x)(ou sem referência se você quiser copiar)
Tamás Szelei
7
Parece-me se você está escrevendo um loop para iterar sobre o conteúdo xe você não sabe mesmo o que xé, você não deve escrever esse ciclo em primeiro lugar ;-)
Nikie
@fish: regras de for-loops baseadas em range, mas eu teria sido pedante e feito: 'for (T & it: x)' ao invés de usar for-loops baseados em range, pois eu acho que usar auto é menos informativo. Uma espécie de uso indevido de automóveis em minha mente.
martiert
A briga pelo uso de var foi um pouco tola, especialmente em retrospecto. Consulte Programação de culto de carga .
Craig

Respostas:

38

As motivações em C ++ são mais extremas, pois os tipos podem se tornar muito mais complicados e complexos do que os tipos C # devido à metaprogramação e outras coisas. autoé mais rápido de escrever e ler e mais flexível / sustentável do que um tipo explícito. Quero dizer, você quer começar a digitar

boost::multi_map<NodeType, indexed_by<ordered_unique<identity<NodeType>>, hashed_non_unique<identity<NodeType>, custom_hasher>>::iterator_type<0> it

Esse nem é o tipo completo. Eu perdi alguns argumentos de modelo.

DeadMG
fonte
8
+1 para o exemplo, mas isso também informa sobre o estado do C ++ "moderno".
Zvrba 12/12/12
22
@zvrba: Sim, as instalações genéricas são muito mais poderosas que as C #.
23712 DeadMG
4
é para isso que serve o typedef #
30812 gbjbaanb
19
@gbjbaanb Não, autoé para isso. Por design. typedefajuda, mas autoajuda mais.
Konrad Rudolph
1
typedef invalida o argumento de que "não usar auto cria para tipos muito longos" #
Michael Michael
28

No seu exemplo:

for(auto it = x.begin(); it != x.end(); i++)
{
  it->??
}

tem que haver uma declaração para xvisível. Portanto, o tipo de itdeve ser óbvio. Se o tipo de xnão for óbvio, o método é muito longo ou a classe é muito grande.

Kevin Cline
fonte
7
Além disso, xé um nome de variável muito ruim para um contêiner. Em algumas situações, você provavelmente pode apenas olhar para o nome (semanticamente valioso) e inferir as operações possíveis.
Max
@ Max: usado apenas xcomo um exemplo genérico, eu tendem a usar nomes de variáveis ​​bastante descritivos.
usuário
@ Usuário É claro que eu não presumi que fosse um exemplo do mundo real;)
Max
14

Objeção ! Pergunta carregada.

Você pode me explicar por que o terceiro código contém ??, mas o primeiro e o segundo não? Por uma questão de justiça, seu código deve ser o seguinte:

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->???
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->second->???
}

Lá. Mesmo problema, mesmo que você não tenha usado auto.

E em todos os casos, a resposta é a mesma: o contexto é importante . Você não pode falar significativamente sobre um pedaço de código isoladamente. Mesmo se você não tivesse usado modelos, mas algum tipo concreto, isso só teria movido o problema para outro lugar, pois o leitor do seu código precisaria saber sobre a declaração desse tipo.

Se o uso autodessas situações tornar seu código ilegível, você deve tratá-lo como um sinal de aviso de que há algo errado com seu design de código. Obviamente, há casos em que detalhes de baixo nível são importantes (como ao lidar com operações de bits ou API herdada), nos quais um tipo explícito pode ajudar na legibilidade. Mas, em geral - não.

Em relação var(desde que você o mencionou explicitamente), também há um vasto consenso na comunidade C # sobre o uso var. Argumentos contra seu uso geralmente são construídos sobre falácias .

Konrad Rudolph
fonte
1
Eu acho que o ponto era que, com auto, você não sabe o que colocar em seguida ... é o código específico "algo" ou é um tipo de dados descompactado para chegar ao seu objeto de dados que tem o método "alguma coisa"
Michael Shaw
1
@Ptolemy E meu argumento é: nos outros dois códigos você também não sabe (geralmente) o que colocar em seguida: Té tão opaco para o usuário quanto auto. No entanto, um deve estar bem e o outro não ?! Isso não faz sentido. No caso do OP, Té um substituto para um tipo arbitrário. No código real, pode ser o uso de modelos (for typename std::vector<T>::iterator…)ou uma interface de classe. Nos dois casos, o tipo real está oculto para o usuário e, no entanto, rotineiramente escrevemos esse código sem problemas.
Konrad Rudolph
1
Na verdade, sim você faz. Se for um vetor, você sabe que precisa fazer -> e então tem acesso ao seu tipo de dados. Se for um mapa, você sabe que precisa fazer -> segundo -> e, em seguida, terá acesso ao seu tipo de dados; se for automático, não sabe o que precisa fazer para obter acesso ao seu tipo de dados. Você parece estar confundindo "qual é o tipo de dados contido na coleção STL" com "qual tipo de coleção STL temos". auto piora esse problema.
Michael Shaw
1
@Ptolemy Todos esses argumentos são verdadeiros ao usar auto. É trivial ver quais operações xsuportam do contexto. Na verdade, o tipo lhe dá nenhuma informação adicional: em ambos os casos você precisa de algum secundário (IDE, documentação, conhecimento / memória) para dizer-lhe o conjunto de operações apoiadas.
Konrad Rudolph
1
@Ptolemy Essa é única verdade se você estiver na situação altamente complicada que você não sabe o beginretorno, mas você não sabe o que std::vector<>::iteratoré. E você precisa usar uma ferramenta de programação ruim que não pode fornecer essas informações trivialmente. Isso é altamente complicado. Na realidade, você quer saber tanto begine iteratorou nenhum, e você deve estar usando um IDE ou editor que pode facilmente tornar a informação relevante disponível para você. Todo editor moderno de IDE e programação pode fazer isso.
Konrad Rudolph
11

PRÓ

Seu código :

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

não será compilado, devido ao nome dependente do modelo.

Esta é a sintaxe correta:

for( typename std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

Agora veja quanto tempo a declaração de tipo é. Isso explica por que a autopalavra - chave foi introduzida. Este :

for( auto it = x.begin(); it != x.end(); i++)

é mais conciso. Então, este é um profissional.


VIGARISTA

Você tem que ter um pouco de cuidado. Com a palavra-chave auto, você obtém o tipo que declarou.

Por exemplo :

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto it : v )
{
  ++ it;   // ops modifying copies of vector's elements
}

vs

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto & it : v )   // mind the reference
{
  ++ it;   // ok, vector's elements modified
}

Para concluir: sim, você deveria, mas não o use demais. Algumas pessoas tendem a usá-lo demais e colocam auto em qualquer lugar, como no próximo exemplo:

auto i = 0;

vs

int i = 0;
BЈовић
fonte
auto i = 0. Culpado. Eu faço isso. Mas isso é porque eu sei que 0é um tipo literal int. (e uma constante octal ;-))
Laurent LA RIZZA 15/02
6

Sim você deveria! autonão apaga o tipo; mesmo se você "não souber" o que x.begin()é, o compilador saberá e relatará um erro se você tentar usar o tipo incorretamente. Além disso, não é incomum emular mapcom a vector<pair<Key,Value>>, portanto, o código usado autofuncionará para as duas representações do dicionário.

zvrba
fonte
4

Sim, você deve usar autocomo regra padrão. Possui vantagens brutas sobre a especificação explícita do tipo:

  • Isso não faz você digitar coisas das quais o compilador já está ciente.
  • Faz com que o tipo de variáveis ​​"acompanhe" qualquer alteração nos tipos de valor de retorno.
  • Isso evita a introdução silenciosa de conversões implícitas e fatias na inicialização de variáveis ​​locais.
  • Isso elimina a necessidade de alguns cálculos de tipo explícito nos modelos.
  • Isso elimina a necessidade de nomear tipos de retorno com nomes longos. (aqueles que você copia e cola dos diagnósticos do compilador)

É aqui que você tem uma escolha. Também há casos em que você não tem escolha:

  • Permite a declaração de variáveis ​​de tipos indizíveis, como o tipo de uma lambda.

Desde que você saiba exatamente o que autofaz, não há desvantagens.

Laurent LA RIZZA
fonte