Parece que auto
foi um recurso bastante significativo a ser adicionado no C ++ 11 que parece seguir muitas das linguagens mais recentes. Como em uma linguagem como Python, não vi nenhuma declaração explícita de variável (não tenho certeza se é possível usar os padrões Python).
Existe uma desvantagem em usar auto
para declarar variáveis em vez de declará-las explicitamente?
c++
c++11
type-inference
auto
DxAlpha
fonte
fonte
Respostas:
Você só perguntou sobre desvantagens, então estou destacando algumas delas. Quando usado bem,
auto
tem várias vantagens também. As desvantagens resultam da facilidade de abuso e do aumento do potencial do código para se comportar de maneira não intencional.A principal desvantagem é que, ao usar
auto
, você não conhece necessariamente o tipo de objeto que está sendo criado. Há também ocasiões em que o programador pode esperar que o compilador deduza um tipo, mas o compilador deduz com firmeza outro.Dada uma declaração como
você não tem necessariamente conhecimento de que tipo
result
é. Pode ser umint
. Pode ser um ponteiro. Pode ser outra coisa. Todos esses suportam operações diferentes. Você também pode alterar drasticamente o código com uma pequena alteração, comoporque, dependendo das sobrecargas existentes para
CallSomeFunction()
o tipo de resultado, pode ser completamente diferente - e, portanto, o código subsequente pode se comportar de maneira totalmente diferente do pretendido. Você pode disparar mensagens de erro repentinamente em códigos posteriores (por exemplo, subseqüentemente tentando desreferenciar umint
, tentando mudar algo que está agoraconst
). A mudança mais sinistra é onde sua mudança passa pelo compilador, mas o código subsequente se comporta de maneiras diferentes e desconhecidas - possivelmente com erros.Não ter conhecimento explícito do tipo de algumas variáveis, portanto, torna mais difícil justificar rigorosamente uma afirmação de que o código funciona como pretendido. Isso significa mais esforço para justificar reivindicações de "adequação à finalidade" em domínios de alta criticidade (por exemplo, crítica à segurança ou crítica à missão).
A outra desvantagem mais comum é a tentação de um programador usar
auto
como um instrumento contundente para forçar a compilação do código, em vez de pensar no que o código está fazendo e trabalhar para corrigi-lo.fonte
auto
, a maioria das linguagens tipadas por pato sofre essa desvantagem por design!CallSomeFunction()
retornar um tipo diferente, dependendo da sequência de seus argumentos, isso é um defeito de designCallSomeFunction()
, não um problema deauto
. Se você não lê a documentação de uma função que está usando antes de usá-la, isso é um defeito do programador, não um problemaauto
. - Mas eu entendo que você está bancando o advogado do diabo aqui, é que Nir Friedman tem um caso muito melhor.T CallSomeFunction(T, int, int)
haveria um defeito de design? Obviamente, "retorna um tipo diferente, dependendo da sequência de seus argumentos".auto
, você não conhece necessariamente o tipo de objeto que está sendo criado." Você pode explicar por que isso é um problemaauto
e não um problema com temporários de subexpressão? Por que éauto result = foo();
ruim, masfoo().bar()
não?Isso não é
auto
exatamente uma desvantagem de princípios, mas em termos práticos parece ser um problema para alguns. Basicamente, algumas pessoas: a) tratamauto
como salvadores de tipos e desligam o cérebro ao usá-lo; ou b) esquecem queauto
sempre deduz aos tipos de valor. Isso faz com que as pessoas façam coisas assim:Opa, nós apenas copiamos profundamente algum objeto. Geralmente, é um bug ou uma falha no desempenho. Então, você pode mudar para o outro lado:
Agora você recebe uma referência pendente. Esses problemas não são causados por
auto
nada, então não os considero argumentos legítimos contra ele. Mas parece queauto
essas questões são mais comuns (por experiência própria), pelas razões que listei no começo.Penso que, com o tempo, as pessoas se ajustarão e entenderão a divisão do trabalho:
auto
deduz o tipo subjacente, mas você ainda deseja pensar em referência e constância. Mas está demorando um pouco.fonte
std::vector
). Ser caro copiar não é propriedade de uma classe, mas de objetos individuais. Portanto,method_that_returns_reference
pode se referir a um objeto de uma classe que possui um construtor de cópias, mas que objeto é muito caro para copiar (e não pode ser movido de).std::vector
? (Porque pode sim, ou porque você não controla a classe, mas esse não é o ponto) Se é caro copiar (e não possui nenhum recurso, porque é copiável), por que não usar COW no objeto? A localidade dos dados já foi eliminada pelo tamanho do objeto.= delete
essa sobrecarga. Embora, geralmente, o que você diz seja uma solução. Este é um tópico que eu explorei, se você estiver interessado: nirfriedman.com/2016/01/18/… .Outras respostas estão mencionando desvantagens como "você realmente não sabe qual é o tipo de variável". Eu diria que isso está amplamente relacionado à convenção de nomes desleixados no código. Se suas interfaces tiverem um nome claro, você não precisará se importar com o tipo exato. Claro,
auto result = callSomeFunction(a, b);
não diz muito. Masauto valid = isValid(xmlFile, schema);
diz o suficiente para você usarvalid
sem ter que se importar com o seu tipo exato. Afinal, com apenasif (callSomeFunction(a, b))
, você também não saberia o tipo. O mesmo com qualquer outro objeto temporário de subexpressão. Portanto, não considero isso uma verdadeira desvantagemauto
.Eu diria que sua principal desvantagem é que, às vezes, o tipo de retorno exato não é o que você deseja trabalhar. De fato, algumas vezes o tipo de retorno real difere do tipo de retorno "lógico" como um detalhe de implementação / otimização. Modelos de expressão são um excelente exemplo. Digamos que temos o seguinte:
Logicamente, esperamos
SomeType
serVector
, e definitivamente queremos tratá-lo como tal em nosso código. No entanto, é possível que, para fins de otimização, a biblioteca de álgebra que estamos usando implementa modelos de expressão, e o tipo de retorno real seja este:Agora, o problema é que
MultExpression<Matrix, Vector>
a vontade em toda a loja de probabilidade de umconst Matrix&
econst Vector&
internamente; espera que seja convertido em aVector
antes do final de sua expressão completa. Se temos esse código, está tudo bem:No entanto, se tivéssemos usado
auto
aqui, poderíamos ter problemas:fonte
auto
tem muito poucas desvantagens, então mantenho essa força. E outros exemplos de proxies etc. incluem vários "construtores de cadeias" e objetos semelhantes encontrados nas DSLs.auto
antes, especificamente com a biblioteca Eigen. É especialmente complicado porque o problema geralmente não aparece nas compilações de depuração.auto
também pode morder ao usar a biblioteca de matriz do Armadillo , que faz uso intenso da meta-programação de modelos para fins de otimização. Felizmente, os desenvolvedores adicionaram a função .eval () que pode ser usada para evitar problemas comauto
auto
geralmente não envolve algum tipo de verificação de tipo (e espirrarauto
em todos os lugares retira esse tipo de segurança, assim como em qualquer outro lugar). Não é uma boa comparação.Uma das desvantagens é que às vezes você não pode declarar
const_iterator
comauto
. Você obterá o iterador comum (não const) neste exemplo de código extraído desta pergunta :fonte
iterator
em qualquer caso, já que seu mapa não éconst
. se você deseja convertê-lo em aconst_iterator
, especifique o tipo de variável explicitamente, como de costume, ou extraia um método para que seu mapa seja const no contexto do seufind
. (Eu prefiro o último SRP..)auto city_it = static_cast<const auto&>(map).find("New York")
? ou, com C ++ 17auto city_if = std::as_const(map).find("New York")
,.Isso torna seu código um pouco mais difícil, ou tedioso, de ler. Imagine algo assim:
Agora, para descobrir o tipo de saída, você teria que rastrear a assinatura da
doSomethingWithData
função.fonte
auto it = vec.begin();
é muito mais fácil de ler do questd::vector<std::wstring>::iterator it = vec.begin();
por exemplo.Como esse desenvolvedor, eu odeio
auto
. Ou melhor, odeio como as pessoas fazem mau usoauto
.Sou da opinião (forte) de que
auto
é para ajudá-lo a escrever código genérico, não para reduzir a digitação .C ++ é uma linguagem cujo objetivo é permitir que você escreva um código robusto, não para minimizar o tempo de desenvolvimento.
Isso é bastante óbvio em muitos recursos do C ++, mas, infelizmente, alguns dos mais novos
auto
reduzem a digitação das pessoas que levam a pensar que devem começar a ficar preguiçosos com a digitação.Nos
auto
dias anteriores , as pessoas usavamtypedef
s, o que foi ótimo, porquetypedef
permitiu ao designer da biblioteca ajudá-lo a descobrir qual deveria ser o tipo de retorno, para que a biblioteca funcionasse conforme o esperado. Quando você usaauto
, tira esse controle do designer da classe e, em vez disso, pede ao compilador para descobrir qual deve ser o tipo, o que remove uma das ferramentas C ++ mais poderosas da caixa de ferramentas e corre o risco de quebrar seu código.Geralmente, se você usar
auto
, deve ser porque seu código funciona para qualquer tipo razoável , não porque você é preguiçoso demais para anotar o tipo com o qual deve trabalhar. Se você usaauto
como uma ferramenta para ajudar a preguiça, o que acontece é que você acaba introduzindo erros sutis em seu programa, geralmente causados por conversões implícitas que não aconteceram porque você o usouauto
.Infelizmente, esses bugs são difíceis de ilustrar em um pequeno exemplo aqui, porque sua brevidade os torna menos convincentes do que os exemplos reais que surgem em um projeto de usuário - no entanto, eles ocorrem facilmente em códigos pesados de modelo que esperam que certas conversões implícitas ocorram Lugar, colocar.
Se você quiser um exemplo, há um aqui . Uma pequena observação: porém, antes de ser tentado a pular e criticar o código: lembre-se de que muitas bibliotecas conhecidas e maduras foram desenvolvidas em torno de conversões implícitas e existem porque resolvem problemas que podem ser difíceis, se não impossíveis. para resolver o contrário. Tente descobrir uma solução melhor antes de criticá-los.
fonte
which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should be
Não é realmente uma boa razão para IMO. IDEs atualizados, Visual Studio 2015, por exemplo, permitem verificar o tipo da variável passando o mouse sobreauto
. Este é exatamente o mesmo quetypedef
esse.typename std::iterator_traits<It>::value_type
. (2) O ponto principal era que o tipo inferido não precisa ser "exatamente o mesmo" que o tipo correto pretendido pelo criador anterior do código; Ao usarauto
, você está diminuindo a capacidade do designer de especificar o tipo correto.vector<bool>
bobagem" ... perdão? Como você acha quebitset
é implementado? Ou você considera que os contêineres de bits são um absurdo?auto
não tem desvantagens em si , e eu advogo que (manualmente) use-o em qualquer lugar do novo código. Ele permite que seu código verifique consistentemente e evite fatias silenciosas. (SeB
deriva deA
e uma função retornandoA
repentinamente retornaB
, entãoauto
se comporta conforme o esperado para armazenar seu valor de retorno)Embora o código legado anterior ao C ++ 11 possa contar com conversões implícitas induzidas pelo uso de variáveis de tipo explícito. Alterar uma variável de tipo explícito para
auto
pode alterar o comportamento do código , por isso é melhor ter cuidado.fonte
auto
tem desvantagens em si (ou pelo menos - muitos pensam que sim). Considere o exemplo dado na segunda pergunta neste painel de discussão com Sutter, Alexandrescu e Meyers: Se você temauto x = foo(); if (x) { bar(); } else { baz(); }
efoo()
retornabool
- o que acontece se asfoo()
alterações retornarem uma enumeração (três opções em vez de duas)? Oauto
código continuará funcionando, mas produzirá resultados inesperados.bool
vez deauto
mudar alguma coisa no caso de um enum sem escopo? Posso estar errado (não é possível verificar aqui), mas acho que a única diferença é que a conversãobool
ocorre na declaração da variável em vez de na avaliação da condição noif
. Se oenum
escopo for definido, a conversão parabool
não ocorrerá sem um aviso explícito.A palavra-chave
auto
simplesmente deduz o tipo do valor retornado. Portanto, não é equivalente a um objeto Python, por exemploComo
auto
é deduzido durante a compilação, ele não terá nenhuma desvantagem no tempo de execução.fonte
type()
em python faz. Deduz o tipo, não cria uma nova variável desse tipo.decltype
.auto
é para atribuição de variável especificamente.O que ninguém mencionou aqui até agora, mas vale por si só uma resposta, se você me perguntasse.
Como (mesmo que todos devam estar cientes disso
C != C++
), o código escrito em C pode ser facilmente projetado para fornecer uma base para o código C ++ e, portanto, ser projetado sem muito esforço para ser compatível com C ++, isso pode ser um requisito para o design.Conheço algumas regras em que algumas construções bem definidas
C
são inválidasC++
e vice-versa. Mas isso resultaria simplesmente em executáveis quebrados e a cláusula UB conhecida se aplica, que na maioria das vezes é notada por ciclos estranhos que resultam em travamentos ou o que for (ou até pode ficar sem ser detectado, mas isso não importa aqui).Mas
auto
é a primeira vez 1 isso muda!Imagine que você usou
auto
como especificador de classe de armazenamento antes e transfira o código. Nem mesmo necessariamente (dependendo da maneira como foi usado) "quebrou"; na verdade, ele pode alterar silenciosamente o comportamento do programa.Isso é algo que se deve ter em mente.
1 Pelo menos na primeira vez que estou ciente.
fonte
int
" em C, merece todas as coisas ruins que conseguirá com isso. E se você não está confiando nele, usarauto
como um especificador de classe de armazenamento ao lado de um tipo fornecerá um erro de compilação agradável em C ++ (que é uma Coisa Boa neste caso).Uma razão pela qual consigo pensar é que você perde a oportunidade de coagir a classe retornada. Se sua função ou método retornou um longo de 64 bits e você só queria um int não assinado de 32, então você perde a oportunidade de controlá-lo.
fonte
Como eu descrevi nesta resposta,
auto
às vezes pode resultar em situações estranhas que você não pretendia. Você tem que dizer explicitamenteauto&
para ter um tipo de referência, enquanto apenasauto
pode criar um tipo de ponteiro. Isso pode resultar em confusão, omitindo todo o especificador, resultando em uma cópia da referência em vez de uma referência real.fonte
auto
acontece, nunca inferindo uma referência nem umconst
tipo. Para umaauto
referência, é melhor você usarauto&&
. (uma referência universal) Se o tipo não for barato para copiar ou possuir um recurso, o tipo não deverá ser copiável, para começar.Outro exemplo irritante:
gera um aviso (
comparison between signed and unsigned integer expressions [-Wsign-compare]
), porquei
é um int assinado. Para evitar isso, você precisa escrever, por exemplo,ou talvez melhor:
fonte
size
retornossize_t
, você teria que ter umsize_t
tipo literal0z
. Mas você pode declarar uma UDL para fazer isso. (size_t operator""_z(...)
)unsigned
é provável que não seja grande o suficiente para conter todos os valores dasstd::size_t
arquiteturas convencionais, portanto, no improvável evento de alguém ter um contêiner com um número absurdamente gigantesco de elementos, o usounsigned
poderia causar um loop infinito no intervalo inferior de índices. Embora seja improvável que seja um problema,std::size_t
deve ser usado para obter um código limpo que sinalize corretamente a intenção. Não tenho certeza seunsigned long long
é estritamente garantido o suficiente, embora, na prática, presumivelmente deva ser o mesmo.unsigned long long
é garantido que seja pelo menos 64 bits, mas em teoriasize_t
poderia ser maior que isso, suponho. Claro, se você tem> 2 ^ 64 elementos em seu recipiente, em seguida, você pode ter problemas maiores para se preocupar ... ;-)Eu acho que
auto
é bom quando usado em um contexto localizado, onde o leitor facilmente e obviamente pode deduzir seu tipo, ou bem documentado com um comentário de seu tipo ou um nome que infere o tipo real. Quem não entende como isso funciona pode levá-lo de maneira errada, como usá-lo em vez detemplate
ou similar. Aqui estão alguns casos de uso bons e ruins na minha opinião.Bons usos
Iteradores
Ponteiros de Função
Maus usos
Fluxo de dados
Função Assinatura
Casos triviais
fonte
int
, você deve digitar mais um caractere se quiserauto
. Isso é inaceitávelint
tão fácil de ver aqui e a digitaçãoint
é mais curta. É por isso que é um caso trivial.Estou surpreso que ninguém tenha mencionado isso, mas suponha que você esteja calculando o fatorial de algo:
Este código produzirá o seguinte:
Esse definitivamente não foi o resultado esperado. Isso aconteceu porque
auto
deduziu o tipo da variável fatorial comoint
porque foi atribuída1
.fonte