Vi uma conferência de Herb Sutter, onde ele incentiva todos os programadores de C ++ a usar auto
.
Eu tive que ler o código C # há algum tempo, onde var
era amplamente utilizado e o código era muito difícil de entender - sempre que var
era usado, era necessário verificar o tipo de retorno do lado direito. Às vezes mais de uma vez, porque esqueci o tipo da variável depois de um tempo!
Eu sei que o compilador conhece o tipo e não preciso escrevê-lo, mas é amplamente aceito que devemos escrever código para programadores, não para compiladores.
Sei também que é mais fácil escrever:
auto x = GetX();
Do que:
someWeirdTemplate<someOtherVeryLongNameType, ...>::someOtherLongType x = GetX();
Mas isso é escrito apenas uma vez e o GetX()
tipo de retorno é verificado várias vezes para entender qual o tipo x
.
Isso me fez pensar - auto
torna o código C ++ mais difícil de entender?
auto
geralmente torna as coisas mais difíceis de ler quando elas já são difíceis de ler, ou seja, funções muito longas, variáveis com nomes inadequados etc. Em funções curtas com variáveis nomeadas decentemente, o conhecimento dos tipos deve ser um dos # 1 fáceis ou # 2 irrelevantes.auto
é como determinar quando usartypedef
. Cabe a você determinar quando isso atrapalha e quando ajuda.auto x = GetX();
, escolha um nome melhor do que ox
que realmente lhe diz o que faz nesse contexto específico ... muitas vezes é mais útil do que seu tipo.Respostas:
Resposta curta: Mais completamente, minha opinião atual
auto
é que você deve usarauto
por padrão, a menos que queira explicitamente uma conversão. (Um pouco mais precisamente, "... a menos que você queira se comprometer explicitamente com um tipo, o que quase sempre ocorre porque você deseja uma conversão.")Resposta e justificativa mais longas:
Escreva um tipo explícito (em vez de
auto
) apenas quando você realmente deseja se comprometer explicitamente com um tipo, o que quase sempre significa que você deseja obter explicitamente uma conversão para esse tipo. De cabeça para baixo, lembro-me de dois casos principais:initializer_list
surpresa queauto x = { 1 };
deduzinitializer_list
. Se você não quiserinitializer_list
, diga o tipo - ou seja, solicite explicitamente uma conversão.auto x = matrix1 * matrix 2 + matrix3;
captura um tipo auxiliar ou proxy, não destinado a ser visível para o programador. Em muitos casos, é bom e benigno capturar esse tipo, mas, às vezes, se você realmente deseja que ele entre em colapso e faça o cálculo, diga o tipo - ou seja, novamente peça explicitamente uma conversão.Rotineiramente, use
auto
por padrão o contrário, porque o usoauto
evita armadilhas e torna seu código mais correto, mais sustentável e robusto e mais eficiente. Aproximadamente da ordem do mais ao menos importante, no espírito de "escreva primeiro para maior clareza e correção":auto
garantias, você obterá o tipo certo. Como diz o ditado, se você se repetir (diga o tipo de forma redundante), você pode e mentirá (entenda errado). Aqui está um exemplo usual:void f( const vector<int>& v ) { for( /*…*
- neste ponto, se você escrever explicitamente o tipo do iterador, lembre-se de escreverconst_iterator
(não é?), Enquantoauto
apenas faz a coisa certa.auto
torna seu código mais robusto diante da mudança, porque quando o tipo da expressão é alterado,auto
a resolução continuará sendo do tipo correto. Se você se comprometer com um tipo explícito, alterar o tipo da expressão injetará conversões silenciosas quando o novo tipo for convertido para o tipo antigo, ou a construção desnecessária será interrompida quando o novo tipo ainda funcionar, como o tipo antigo, mas não se converter no antigo. tipo (por exemplo, quando você altera ummap
para umunordered_map
, o que é sempre bom se você não está confiando no pedido, usandoauto
para seus iteradores você alternará perfeitamente demap<>::iterator
paraunordered_map<>::iterator
, mas usandomap<>::iterator
em todos os lugares, explicitamente, significa que você estará desperdiçando seu valioso tempo em uma onda mecânica de correção de código, a menos que um estagiário esteja passando e você possa impor o trabalho chato neles).auto
garante que nenhuma conversão implícita ocorrerá, garante um melhor desempenho por padrão. Se, em vez disso, você digitar o tipo, e ele exigir uma conversão, geralmente você receberá uma conversão silenciosamente, independentemente de ter esperado ou não.auto
é sua única boa opção para tipos difíceis de soletrar e indizíveis, como lambdas e auxiliares de modelos, além de recorrer adecltype
expressões repetitivas ou indiretas menos eficientes, comostd::function
.auto
é menos digitado. Menciono isso por último, porque é um motivo comum para gostar, mas não é o maior motivo para usá-lo.Portanto: Prefira dizer
auto
por padrão. Ele oferece tanta simplicidade, desempenho e clareza que você só se machuca (e os futuros mantenedores do seu código) se não o fizer. Confirme apenas um tipo explícito quando você realmente quiser, o que quase sempre significa que você deseja uma conversão explícita.Sim, existe (agora) um GotW sobre isso.
fonte
auto x = static_cast<X>(y)
. Ostatic_cast
deixa claro que a conversão é de propósito e evita avisos do compilador sobre a conversão. Normalmente, evitar avisos do compilador não é tão bom, mas não posso receber um aviso sobre uma conversão que considerei cuidadosamente ao escreverstatic_cast
. Embora eu não faça isso se não houver avisos agora, mas quero receber avisos no futuro se os tipos mudarem de uma maneira potencialmente perigosa.auto
é que devemos nos esforçar para programar contra interfaces (não no sentido de POO), não contra implementações específicas. É o mesmo com os modelos, realmente. Você reclama de "código difícil de ler" porque possui um parâmetro de tipo de modeloT
usado em qualquer lugar? Não, eu não penso assim. Nos modelos também codificamos contra uma interface, a digitação com patos em tempo de compilação é o que muitas pessoas chamam.auto
.auto
variável, mas quase todos eles fazem isso corretamente com especificação explícita de tipo. Ninguém usa IDE? Todo mundo usa apenas variáveis int / float / bool? Todo mundo prefere documentação externa para bibliotecas em vez de cabeçalhos auto-documentados?=
RHS não fazem muito sentido em nenhuma outra interpretação (lista de chaves em barra, mas você precisa saber o que está inicializando, que é um oxímoroauto
). O que é surpreendenteauto i{1}
também está deduzindoinitializer_list
, apesar de implicar não pegar essa lista de inicialização, mas pegar essa expressão e usar seu tipo ... mas chegamosinitializer_list
lá também. Felizmente, o C ++ 17 corrige tudo isso bem.É uma situação caso a caso.
Às vezes, torna o código mais difícil de entender, às vezes não. Tomemos, por exemplo:
é definitivamente fácil de entender e definitivamente mais fácil de escrever do que a declaração do iterador real.
Estou usando C ++ há algum tempo, mas posso garantir que receberia um erro de compilador na minha primeira tentativa, porque esqueceria o
const_iterator
e inicialmente iria para oiterator
... :)Eu o usaria para casos como esse, mas não onde ele ofusque o tipo (como sua situação), mas isso é puramente subjetivo.
fonte
std::map<int, std::string>::const_iterator
, portanto, não é como se o nome lhe dissesse muito sobre o tipo.int
e o valor éstd::string
. :)it->second
pois é um const iterador. Todas essas informações são uma repetição do que está na linha anteriorconst std::map<int, std::string>& x
,. Dizendo coisas várias vezes que ocasionalmente informar melhor, mas nem por isso é que um :-) regra geralfor (anX : x)
deixar ainda mais óbvio que estamos apenas repetindox
. O caso normal em que você precisa de um iterador é quando você está modificando o contêiner, masx
éconst&
Olhe de outra maneira. Tu escreves:
ou:
Às vezes, não ajuda digitar o tipo explicitamente.
A decisão se você precisa mencionar o tipo não é a mesma que a decisão se você deseja dividir o código em várias instruções definindo variáveis intermediárias. No C ++ 03, os dois estavam vinculados, você pode pensar
auto
em uma maneira de separá-los.Às vezes, tornar os tipos explícitos pode ser útil:
vs.
Nos casos em que você declara uma variável, usar
auto
permite que o tipo não seja dito da mesma maneira que ocorre em muitas expressões. Você provavelmente deve tentar decidir por si mesmo quando isso ajuda na legibilidade e quando isso atrapalha.Você pode argumentar que misturar tipos assinados e não assinados é um erro para começar (de fato, alguns argumentam ainda que não se deve usar tipos não assinados). A razão pela qual é indiscutivelmente um erro é que torna os tipos de operandos de vital importância por causa do comportamento diferente. Se é uma coisa ruim precisar conhecer os tipos de seus valores, provavelmente também não é uma coisa ruim não precisar conhecê-los. Portanto, desde que o código já não esteja confuso por outros motivos, tudo
auto
bem, certo? ;-)Particularmente ao escrever código genérico, há casos em que o tipo real de uma variável não deve ser importante, o que importa é que ela satisfaça a interface necessária. Portanto,
auto
fornece um nível de abstração em que você ignora o tipo (mas é claro que o compilador não sabe disso). Trabalhar em um nível adequado de abstração pode ajudar bastante a legibilidade, trabalhar no nível "errado" torna a leitura do código uma tarefa árdua.fonte
auto
permite criar variáveis nomeadas com tipos inomináveis ou desinteressantes. Nomes significativos podem ser úteis.sizeof
como não assinado em você.Na IMO, você está olhando isso de maneira inversa.
Não se trata de
auto
levar a códigos ilegíveis ou até menos legíveis. É uma questão de (esperar que) ter um tipo explícito para o valor de retorno compensar o fato de que (aparentemente) não está claro que tipo seria retornado por alguma função específica.Pelo menos na minha opinião, se você tem uma função cujo tipo de retorno não é imediatamente óbvio, esse é o seu problema. O que a função faz deve ser óbvio pelo nome e o tipo do valor de retorno deve ser óbvio pelo que faz. Caso contrário, essa é a verdadeira fonte do problema.
Se houver um problema aqui, não é com
auto
. É com o restante do código, e as chances são muito boas de que o tipo explícito seja apenas um band-aid para impedir que você veja e / ou corrija o problema principal. Depois de corrigir o problema real, a legibilidade do códigoauto
geralmente será ótima.Suponho, com justiça, que devo acrescentar: lidei com alguns casos em que essas coisas não eram tão óbvias quanto você gostaria, e corrigir o problema também era bastante insustentável. Apenas por um exemplo, eu fiz algumas consultorias para uma empresa há alguns anos, que haviam se fundido anteriormente com outra empresa. Eles acabaram com uma base de código mais "unida" do que realmente mesclada. Os programas constituintes começaram a usar bibliotecas diferentes (mas bastante semelhantes) para propósitos semelhantes e, embora estivessem trabalhando para mesclar as coisas de maneira mais limpa, ainda o faziam. Em um número razoável de casos, a única maneira de adivinhar que tipo seria retornado por uma determinada função era saber onde essa função se originou.
Mesmo nesse caso, você pode ajudar a esclarecer algumas coisas. Nesse caso, todo o código começou no espaço de nomes global. Simplesmente mover uma quantidade razoável para alguns namespaces eliminou os conflitos de nomes e facilitou bastante o rastreamento de tipos.
fonte
Existem várias razões pelas quais eu não gosto de auto para uso geral:
Mas espere, isso é realmente uma boa ideia? E se o tipo importasse em meia dúzia desses casos de uso e agora esse código realmente se comporte de maneira diferente? Isso também pode implicitamente quebrar o encapsulamento, modificando não apenas os valores de entrada, mas o próprio comportamento da implementação privada de outras classes que chamam a função.
1a. Eu acredito no conceito de "código de auto-documentação". O raciocínio por trás do código de auto-documentação é que os comentários tendem a ficar desatualizados, não refletindo mais o que o código está fazendo, enquanto o próprio código - se escrito de maneira explícita - é auto-explicativo, sempre atualizado por sua intenção e não o deixará confuso com comentários obsoletos. Se os tipos puderem ser alterados sem a necessidade de modificar o próprio código, os próprios códigos / variáveis poderão ficar obsoletos. Por exemplo:
auto bThreadOK = CheckThreadHealth ();
Exceto que o problema é que CheckThreadHealth () em algum momento foi refatorado para retornar um valor de enumeração indicando o status do erro, se houver, em vez de um bool. Mas a pessoa que fez essa alteração deixou de inspecionar essa linha de código específica e o compilador não ajudou em nada porque foi compilado sem avisos ou erros.
Provavelmente até funciona. Eu digo que funciona, porque mesmo que você esteja fazendo uma cópia de uma estrutura de 500 bytes para cada iteração de loop, para que você possa inspecionar um único valor, o código ainda está completamente funcional. Portanto, mesmo seus testes de unidade não ajudam você a perceber que um código incorreto está escondido atrás desse automóvel simples e de aparência inocente. A maioria das outras pessoas que escaneiam o arquivo também não notará à primeira vista.
Isso também pode ser agravado se você não souber qual é o tipo, mas escolher um nome de variável que faça uma suposição errada sobre o que é, atingindo o mesmo resultado que em 1a, mas desde o início, em vez de pós-refatoração.
Parece-me óbvio que o auto foi introduzido principalmente como uma solução alternativa para uma sintaxe terrível com os tipos de modelo de biblioteca padrão. Em vez de tentar corrigir a sintaxe do modelo com a qual as pessoas já estão familiarizadas - o que também pode ser quase impossível de executar devido a todo o código existente que pode ser quebrado -, adicione uma palavra-chave que basicamente oculte o problema. Essencialmente, o que você pode chamar de "hack".
Na verdade, eu não tenho nenhum desacordo com o uso de auto com contêineres de biblioteca padrão. Obviamente, é para isso que a palavra-chave foi criada, e as funções da biblioteca padrão provavelmente não mudam fundamentalmente de propósito (ou de tipo), tornando o automóvel relativamente seguro. Mas eu seria muito cauteloso ao usá-lo com seu próprio código e interfaces que podem ser muito mais voláteis e potencialmente sujeitas a mudanças mais fundamentais.
Outra aplicação útil do auto que aprimora a capacidade do idioma é criar temporários em macros agnósticas de tipo. Isso é algo que você realmente não podia fazer antes, mas pode fazê-lo agora.
fonte
auto something = std::make_shared<TypeWithLongName<SomeParam>>(a,b,c);
. :-)Sim, facilita o conhecimento do tipo de sua variável, se você não usar
auto
. A questão é: você precisa saber o tipo de sua variável para ler o código? Às vezes a resposta será sim, às vezes não. Por exemplo, ao obter um iterador de astd::vector<int>
, você precisa saber que é umstd::vector<int>::iterator
ou seriaauto iterator = ...;
suficiente? Tudo o que alguém gostaria de fazer com um iterador é dado pelo fato de ser um iterador - simplesmente não importa qual é o tipo específico.Use
auto
nessas situações em que não dificulta a leitura do seu código.fonte
Pessoalmente, uso
auto
apenas quando é absolutamente óbvio para o programador o que é.Exemplo 1
Exemplo 2
fonte
auto record = myObj.FindRecord(something)
, ficaria claro que o tipo de variável era registro. Ou nomeá-loit
ou semelhante deixaria claro que ele retorna um iterador. Observe que, mesmo que você não tenha usadoauto
, nomear adequadamente a variável significaria que você não precisa voltar à declaração para olhar o tipo de qualquer lugar da função . Eu removi meu voto negativo, porque o exemplo não é um completo idiota agora, mas ainda não comprei o argumento aqui.MyClass::RecordTy record = myObj.FindRecord (something)
Esta pergunta solicita opinião, que variará de programador para programador, mas eu diria que não. De fato, em muitos casos, exatamente o oposto,
auto
pode ajudar a tornar o código mais fácil de entender, permitindo que o programador se concentre na lógica e não nas minúcias.Isto é especialmente verdade em face de tipos de modelos complexos. Aqui está um exemplo simplificado e artificial. O que é mais fácil de entender?
.. ou ...
Alguns diriam que o segundo é mais fácil de entender, outros podem dizer o primeiro. Ainda outros podem dizer que o uso gratuito de
auto
pode contribuir para emburrecer os programadores que o usam, mas isso é outra história.fonte
std::map
exemplos, além de argumentos de modelo complexos.map
s. :)for
por exemplo, se os iteradores são invalidados no corpo do loop e, portanto, precisam ser pré-incrementados ou nem incrementados.Até agora, muitas boas respostas, mas para focar na pergunta original, acho que Herb vai longe demais em seus conselhos para usar
auto
liberalmente. Seu exemplo é um caso em que o usoauto
obviamente prejudica a legibilidade. Algumas pessoas insistem que não é um problema com os IDEs modernos, nos quais você pode passar o mouse sobre uma variável e ver o tipo, mas eu discordo: mesmo as pessoas que sempre usam um IDE às vezes precisam olhar trechos de código isoladamente (pense em revisões de código , por exemplo) e um IDE não ajudará.Conclusão: use
auto
quando ajudar: ou seja, iteradores para loops. Não o utilize quando isso fizer com que o leitor lute para descobrir o tipo.fonte
Estou bastante surpreso que ninguém apontou ainda que o automóvel ajuda se não houver um tipo claro. Nesse caso, você pode solucionar esse problema usando um #define ou um typedef em um modelo para encontrar o tipo utilizável real (e isso às vezes não é trivial) ou apenas usa automático.
Suponha que você tenha uma função que retorne algo com o tipo específico de plataforma:
Qual bruxa você prefere?
ou simplesmente
Claro, você pode escrever
bem em algum lugar, mas faz
realmente diz algo mais sobre o tipo de x? Diz que é o que é retornado de lá, mas é exatamente o que é o automóvel. Isso é redundante - 'coisas' são escritas 3 vezes aqui - na minha opinião, isso torna menos legível que a versão 'automática'.
fonte
typedef
eles.A legibilidade é subjetiva; você precisará analisar a situação e decidir o que é melhor.
Como você apontou, sem auto, longas declarações podem produzir muita confusão. Mas, como você também apontou, declarações breves podem remover informações de tipo que podem ser valiosas.
Além disso, eu também acrescentaria: certifique-se de observar a legibilidade e não a capacidade de gravação. Código fácil de escrever geralmente não é fácil de ler e vice-versa. Por exemplo, se eu estivesse escrevendo, preferiria o automático. Se eu estivesse lendo, talvez as declarações mais longas.
Depois, há consistência; Quão importante é isso para você? Deseja auto em algumas partes e declarações explícitas em outras, ou um método consistente por toda parte?
fonte
Considerarei o código menos legível como uma vantagem e incentivarei o programador a usá-lo cada vez mais. Por quê? Claramente, se o código que usa auto é difícil de ler, também será difícil escrever. O programador é forçado a usar o nome significativo da variável , para melhorar seu trabalho.
Talvez no começo o programador não consiga escrever os nomes significativos das variáveis. Mas, eventualmente, enquanto corrige os bugs, ou na revisão de código, quando ele / ela precisa explicar o código para outras pessoas, ou em um futuro não tão próximo, ele / ela explica o código para as pessoas de manutenção, o programador percebe o erro e usa o nome da variável significativa no futuro.
fonte
myComplexDerivedType
para compensar o tipo ausente, o que atrapalha o código pela repetição de tipo (em todos os lugares em que a variável é usada) e leva as pessoas a omitir o objetivo da variável em seu nome. . Minha experiência é que não há nada tão improdutivo quanto colocar ativamente obstáculos no código.Eu tenho duas diretrizes:
Se o tipo da variável for óbvio, tedioso para escrever ou difícil determinar, use auto.
Se você precisar de uma conversão específica ou o tipo de resultado não for óbvio e poderá causar confusão.
fonte
Sim. Diminui a verbosidade, mas o mal-entendido comum é que a verbosidade diminui a legibilidade. Isso só é verdade se você considerar a legibilidade estética, em vez de sua capacidade real de interpretar código - o que não é aumentado usando o auto. No exemplo mais comumente citado, iteradores de vetor, pode parecer na superfície que o uso de auto aumenta a legibilidade do seu código. Por outro lado, nem sempre você sabe o que a palavra-chave automática fornecerá. Você precisa seguir o mesmo caminho lógico que o compilador faz para fazer essa reconstrução interna e, na maioria das vezes, principalmente nos iteradores, você fará as suposições erradas.
No final do dia, 'auto' sacrifica a legibilidade do código e a clareza, para a 'limpeza' sintática e estética (o que é necessária apenas porque os iteradores têm sintaxe desnecessariamente complicada) e a capacidade de digitar 10 caracteres a menos em qualquer linha. Não vale a pena o risco ou o esforço envolvido a longo prazo.
fonte