Auto torna o código C ++ mais difícil de entender?

122

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 varera amplamente utilizado e o código era muito difícil de entender - sempre que varera 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 - autotorna o código C ++ mais difícil de entender?

Mircea Ispas
fonte
29
Você realmente precisa verificar o tipo de retorno sempre ? Por que o tipo não está claro no código? autogeralmente 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.
R. Martinho Fernandes
25
A "arte" de usar autoé como determinar quando usar typedef. Cabe a você determinar quando isso atrapalha e quando ajuda.
ahenderson
18
Eu pensei que tinha o mesmo problema, mas depois percebi que podia entender o código sem conhecer os tipos. por exemplo: "auto idx = get_index ();" então idx é algo que contém um índice. Qual é o tipo exato, é bastante irrelevante para a maioria dos casos.
PlasmaHH
31
Portanto, não escreva auto x = GetX();, escolha um nome melhor do que o xque realmente lhe diz o que faz nesse contexto específico ... muitas vezes é mais útil do que seu tipo.
Jonathan Wakely
11
Se o uso de mais inferência de tipo dificulta a leitura do código, o código ou o programador precisam ser aprimorados seriamente.
CA McCann

Respostas:

100

Resposta curta: Mais completamente, minha opinião atual autoé que você deve usar autopor 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:

  • (Comum) A initializer_listsurpresa que auto x = { 1 };deduz initializer_list. Se você não quiser initializer_list, diga o tipo - ou seja, solicite explicitamente uma conversão.
  • (Raro) O caso dos modelos de expressão, como o que 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 autopor padrão o contrário, porque o uso autoevita 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":

  • Correção: usando autogarantias, 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 escrever const_iterator(não é?), Enquanto autoapenas faz a coisa certa.
  • Manutenção e robustez: o uso autotorna seu código mais robusto diante da mudança, porque quando o tipo da expressão é alterado, autoa 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 um mappara um unordered_map, o que é sempre bom se você não está confiando no pedido, usando autopara seus iteradores você alternará perfeitamente de map<>::iteratorpara unordered_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).
  • Desempenho: como autogarante 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.
  • Usabilidade: Usar 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 a decltypeexpressões repetitivas ou indiretas menos eficientes, como std::function.
  • Conveniência: E, sim, 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 autopor 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.

Herb Sutter
fonte
14
Acho auto útil mesmo quando eu quero uma conversão. Ele me permite pedir explicitamente uma conversão sem repetir o tipo: auto x = static_cast<X>(y). O static_castdeixa 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 escrever static_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.
Bjarke Hammersholt Roune
6
Uma coisa que eu acho 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 modelo Tusado 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.
Xeo 27/12/12
6
"Usando garantias automáticas, você obterá o tipo certo." Não é verdade. Apenas garante que você obterá o tipo prescrito por outra parte do seu código. Se isso é certo ou não, não é totalmente claro quando você o esconde atrás auto.
Lightness Races in Orbit
Estou realmente surpreso que ninguém se preocupe com IDEs ... Mesmo os IDEs modernos não suportam corretamente o salto para a definição de classe / estrutura no caso de autovariá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?
Avtomaton 26/01/19
que GotW: herbsutter.com/2013/08/12/… não vejo como essa "surpresa inicializador_list" é uma surpresa; chaves em torno do =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ímoro auto). O que é surpreendente auto i{1}também está deduzindo initializer_list, apesar de implicar não pegar essa lista de inicialização, mas pegar essa expressão e usar seu tipo ... mas chegamos initializer_listlá também. Felizmente, o C ++ 17 corrige tudo isso bem.
Underscore_d
112

É uma situação caso a caso.

Às vezes, torna o código mais difícil de entender, às vezes não. Tomemos, por exemplo:

void foo(const std::map<int, std::string>& x)
{
   for ( auto it = x.begin() ; it != x.end() ; it++ )
   { 
       //....
   }
}

é 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_iteratore inicialmente iria para o iterator... :)

Eu o usaria para casos como esse, mas não onde ele ofusque o tipo (como sua situação), mas isso é puramente subjetivo.

Luchian Grigore
fonte
45
Exatamente. Quem diabos se importa com o tipo. É um iterador. Não me importo com o tipo, tudo que preciso saber é que posso usá-lo para iterar.
R. Martinho Fernandes
5
+1. Mesmo se você nomeasse o tipo, nomearia como std::map<int, std::string>::const_iterator, portanto, não é como se o nome lhe dissesse muito sobre o tipo.
precisa
4
@ SteveJessop: Diz-me pelo menos duas coisas: a chave é inte o valor é std::string. :)
Nawaz
16
@Nawaz: e que você não pode atribuir, it->secondpois é um const iterador. Todas essas informações são uma repetição do que está na linha anterior const std::map<int, std::string>& x,. Dizendo coisas várias vezes que ocasionalmente informar melhor, mas nem por isso é que um :-) regra geral
Steve Jessop
11
Eu preferiria for (anX : x)deixar ainda mais óbvio que estamos apenas repetindo x. O caso normal em que você precisa de um iterador é quando você está modificando o contêiner, mas xéconst&
MSalters
94

Olhe de outra maneira. Tu escreves:

std::cout << (foo() + bar()) << "\n";

ou:

// it is important to know the types of these values
int f = foo();
size_t b = bar();
size_t total = f + b;

std::cout << total << "\n";

À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 autoem uma maneira de separá-los.

Às vezes, tornar os tipos explícitos pode ser útil:

// seems legit    
if (foo() < bar()) { ... }

vs.

// ah, there's something tricky going on here, a mixed comparison
if ((unsigned int)foo() < bar()) { ... }

Nos casos em que você declara uma variável, usar autopermite 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 autobem, 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, autofornece 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.

Steve Jessop
fonte
21
O +1 autopermite criar variáveis ​​nomeadas com tipos inomináveis ​​ou desinteressantes. Nomes significativos podem ser úteis.
Mankarse
Misturando assinado e não assinado, se você não assinar para o uso correto: aritmética modular. Não é se você usar indevidamente o sinal para um número inteiro positivo. Quase nenhum programa pode ser usado como não assinado, mas o idioma principal força sua definição insana de sizeofcomo não assinado em você.
precisa
27

Na IMO, você está olhando isso de maneira inversa.

Não se trata de autolevar 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ódigo autogeralmente 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.

Jerry Coffin
fonte
17

Existem várias razões pelas quais eu não gosto de auto para uso geral:

  1. Você pode refatorar o código sem modificá-lo. Sim, essa é uma das coisas frequentemente listadas como um benefício do uso automático. Apenas altere o tipo de retorno de uma função e, se todo o código que a chama usar automático, nenhum esforço adicional será necessário! Você pressiona a compilação, cria - 0 avisos, 0 erros - e basta ir em frente e verificar seu código sem ter que lidar com a bagunça de olhar e modificar potencialmente os 80 locais em que a função é usada.

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.

  1. Você pode nunca saber quais são os tipos reais. Isso também é frequentemente listado como um "benefício" primário do automóvel. Por que aprender o que uma função está lhe dando, quando você pode simplesmente dizer: "Quem se importa? Compila!"

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.

  1. Digitar o código ao escrever inicialmente não é a parte da programação que consome mais tempo. Sim, o modo automático torna a escrita de código mais rápida inicialmente. Como isenção de responsabilidade, digito> 100 WPM, então talvez isso não me incomode tanto quanto os outros. Mas se tudo o que eu precisava fazer era escrever um novo código o dia inteiro, eu seria um feliz campista. A parte da programação que consome mais tempo é diagnosticar erros de borda difíceis de reproduzir no código, geralmente resultantes de problemas sutis e não óbvios - como é provável que o uso excessivo de auto introduza (referência x cópia, assinado x não assinado, float x int, bool x ponteiro etc.).

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.

Tim W
fonte
4
Você acertou em cheio. Gostaria de poder dar a isso um +2.
C26
Uma boa resposta "seja muito cauteloso". @ master: Aqui está.
Deduplicator
Eu descobri caso mais uma útil: auto something = std::make_shared<TypeWithLongName<SomeParam>>(a,b,c);. :-)
Notinlist 17/11/2015
14

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 a std::vector<int>, você precisa saber que é um std::vector<int>::iteratorou seria auto 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 autonessas situações em que não dificulta a leitura do seu código.

Joseph Mansfield
fonte
12

Pessoalmente, uso autoapenas quando é absolutamente óbvio para o programador o que é.

Exemplo 1

std::map <KeyClass, ValueClass> m;
// ...
auto I = m.find (something); // OK, find returns an iterator, everyone knows that

Exemplo 2

MyClass myObj;
auto ret = myObj.FindRecord (something)// NOT OK, everyone needs to go and check what FindRecord returns

fonte
5
Este é um exemplo claro de má nomeação prejudicando a legibilidade, não realmente automático. Ninguém tem a menor idéia do que "DoSomethingWeird" faz; portanto, usar auto ou não não será mais legível. Você precisará verificar os documentos de qualquer maneira.
R. Martinho Fernandes
4
Ok, está um pouco melhor agora. Eu ainda acho que a variável é mal nomeada, no entanto, o que ainda dói. Se você escrevesse auto record = myObj.FindRecord(something), ficaria claro que o tipo de variável era registro. Ou nomeá-lo itou semelhante deixaria claro que ele retorna um iterador. Observe que, mesmo que você não tenha usado auto, 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.
R. Martinho Fernandes
2
Para adicionar a R.MartinhoFernandes: a questão é: isso é realmente importante agora O QUE É UM "registro" exatamente? É eu acho que mais importante que isso é um recorde, o tipo primitivo subjacente real é outra camada de abstração .. Então seria sem auto provavelmente tem:MyClass::RecordTy record = myObj.FindRecord (something)
paul23
2
@ paul23: O que você faz usando auto versus tipo, você ganha se sua única objeção é "Não sei como usar isso". Ou faz você procurar assim mesmo.
GManNickG
3
@GManNickG, diz-me o tipo exato de falta de importância.
paul23
10

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, autopode 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?

for( std::map<std::pair<Foo,Bar>, std::pair<Baz, Bot>, std::less<BazBot>>::const_iterator it = things_.begin(); it != things_.end(); ++it )

.. ou ...

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

Alguns diriam que o segundo é mais fácil de entender, outros podem dizer o primeiro. Ainda outros podem dizer que o uso gratuito de autopode contribuir para emburrecer os programadores que o usam, mas isso é outra história.

John Dibling
fonte
4
+1 Haha, todo mundo está apresentando std::mapexemplos, além de argumentos de modelo complexos.
Nawaz
1
@Nawaz: É fácil criar nomes de modelos muito longos usando maps. :)
John Dibling
@Nawaz: mas eu me pergunto por que, então ninguém está vindo com intervalo com base em laços como a alternativa melhor e mais legível ...
PlasmaHH
1
@PlasmaHH, nem todos os loops com iteradores podem ser substituídos por baseados em intervalo, forpor exemplo, se os iteradores são invalidados no corpo do loop e, portanto, precisam ser pré-incrementados ou nem incrementados.
Jonathan Wakely
@PlasmaHH: No meu caso, o MSVC10 não faz loops baseados em intervalo. Como o MSVC10 é o meu leito de teste C ++ 11, eu realmente não tenho muita experiência com eles.
John Dibling
8

Até agora, muitas boas respostas, mas para focar na pergunta original, acho que Herb vai longe demais em seus conselhos para usar autoliberalmente. Seu exemplo é um caso em que o uso autoobviamente 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 autoquando ajudar: ou seja, iteradores para loops. Não o utilize quando isso fizer com que o leitor lute para descobrir o tipo.

Nemanja Trifunovic
fonte
6

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:

#ifdef PLATFROM1
__int256 getStuff();
#else //PLATFORM2
__int128 getStuff();
#endif

Qual bruxa você prefere?

#ifdef PLATFORM1
__int256 stuff = getStuff();
#else
__int128 stuff = getStuff();
#endif

ou simplesmente

auto stuff = getStuff();

Claro, você pode escrever

#define StuffType (...)

bem em algum lugar, mas faz

StuffType stuff = getStuff();

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

Lrdx
fonte
5
A maneira correta de lidar com tipos específicos de plataforma é para typedefeles.
C26
3

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
2

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.

Manoj R
fonte
2
Na melhor das hipóteses, seria necessário que as pessoas escrevessem nomes de variáveis myComplexDerivedTypepara 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.
C26
2

Eu tenho duas diretrizes:

  • Se o tipo da variável for óbvio, tedioso para escrever ou difícil determinar, use auto.

    auto range = 10.0f; // Obvious
    
    for (auto i = collection.cbegin(); i != cbegin(); ++i) // Tedious if collection type
    // is really long
    
    template <typename T> ... T t; auto result = t.get(); // Hard to determine as get()
    // might return various stuff
  • Se você precisar de uma conversão específica ou o tipo de resultado não for óbvio e poderá causar confusão.

    class B : A {}; A* foo = new B(); // 'Convert'
    
    class Factory { public: int foo(); float bar(); }; int f = foo(); // Not obvious
XIII vermelho
fonte
0

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.

metamorfose
fonte