Eu posso ver por que o auto
tipo em C ++ 11 melhora a correção e a manutenção. Eu li que ele também pode melhorar o desempenho ( Quase sempre automático de Herb Sutter), mas sinto falta de uma boa explicação.
- Como pode
auto
melhorar o desempenho? - Alguém pode dar um exemplo?
c++
performance
c++11
auto
DaBrain
fonte
fonte
Respostas:
auto
pode ajudar no desempenho, evitando conversões implícitas silenciosas . Um exemplo que considero atraente é o seguinte.Vê o bug? Aqui estamos, pensando que estamos usando elegantemente todos os itens do mapa por referência const e usando a nova expressão range-for para tornar nossa intenção clara, mas na verdade estamos copiando todos os elementos. Isto é porque
std::map<Key, Val>::value_type
éstd::pair<const Key, Val>
, nãostd::pair<Key, Val>
. Assim, quando nós (implicitamente) temos:Em vez de pegar uma referência a um objeto existente e deixá-lo assim, precisamos fazer uma conversão de tipo. Você tem permissão para fazer uma referência const a um objeto (ou temporário) de um tipo diferente, desde que haja uma conversão implícita disponível, por exemplo:
A conversão de tipo é uma conversão implícita permitida pelo mesmo motivo pelo qual você pode converter a
const Key
em aKey
, mas precisamos construir um temporário do novo tipo para permitir isso. Assim, efetivamente nosso loop:(É claro que não há realmente um
__tmp
objeto, ele está lá apenas para ilustração; na realidade, o temporário sem nome está vinculado apenasitem
por toda a sua vida).Apenas alterando para:
nos salvou uma tonelada de cópias - agora o tipo referenciado corresponde ao tipo do inicializador, portanto, nenhuma conversão ou temporária é necessária, podemos apenas fazer uma referência direta.
fonte
std::pair<const Key, Val> const &
como umstd::pair<Key, Val> const &
? Novo no C ++ 11, não tenho certeza de como o range-for e seauto
encaixa nisso.auto
esse aumento de desempenho. Então, eu vou escrever com minhas próprias palavras abaixo.auto
melhora o desempenho". É apenas um exemplo que "auto
ajuda a evitar erros de programadores que destroem o desempenho". Eu afirmo que há uma distinção sutil, mas importante, entre os dois. Ainda assim, +1.Como
auto
deduz o tipo da expressão de inicialização, não há conversão de tipo envolvida. Combinado com algoritmos de modelo, isso significa que você pode obter uma computação mais direta do que se você fosse criar um tipo - especialmente quando estiver lidando com expressões cujo tipo você não pode nomear!Um exemplo típico vem de (ab) usando
std::function
:Com
cmp2
ecmp3
, todo o algoritmo pode alinhar a chamada de comparação, enquanto que, se você construir umstd::function
objeto, a chamada não pode ser incorporada, mas também é necessário passar pela pesquisa polimórfica no interior do invólucro da função que foi apagado por tipo.Outra variante desse tema é que você pode dizer:
Essa é sempre uma referência, vinculada ao valor da expressão de chamada de função e nunca constrói nenhum objeto adicional. Se você não conhecia o tipo do valor retornado, pode ser forçado a construir um novo objeto (talvez temporário) por meio de algo como
T && f = MakeAThing()
. (Além disso,auto &&
funciona mesmo quando o tipo de retorno não é móvel e o valor de retorno é um pré-valor.)fonte
auto
. Sua outra variante é "evitar cópias acidentais", mas precisa de detalhes; por queauto
você acelera simplesmente digitando o tipo lá? (Acho que a resposta é "você errou o tipo e ele silenciosamente se converte"). O que o torna um exemplo menos bem explicado da resposta de Barry, não? Ou seja, existem dois casos básicos: automático para evitar o apagamento de tipo e automático para evitar erros de tipo silencioso que são convertidos acidentalmente, ambos com custo de tempo de execução.std::bind
,std::function
estd::stable_partition
todos foram inlined? Ou apenas que, na prática, nenhum compilador C ++ será incorporado de forma agressiva o suficiente para resolver a bagunça?std::function
construtor, será muito complexo ver a chamada real, especialmente com otimizações de pequenas funções (para que você realmente não deseje desirtualização). Claro que, em princípio, tudo é como se ...Existem duas categorias.
auto
pode evitar o apagamento do tipo. Existem tipos inomináveis (como lambdas) e tipos quase inomináveis (como o resultado destd::bind
ou outro modelo de expressão semelhante a coisas).Sem
auto
, você acaba digitando apagar os dados para algo parecidostd::function
. O apagamento do tipo tem custos.task1
possui sobrecarga de apagamento de tipo - uma possível alocação de heap, dificuldade em incluí-la e sobrecarga de invocação de tabela de função virtual.task2
não tem nenhum. Lambdas precisam de deduções automáticas ou outras formas de dedução de tipo para armazenar sem apagamento de tipo; outros tipos podem ser tão complexos que só precisam dele na prática.Segundo, você pode errar tipos. Em alguns casos, o tipo errado funcionará aparentemente perfeitamente, mas causará uma cópia.
irá compilar se
expression()
retornosBar const&
ouBar
mesmoBar&
, de ondeFoo
pode ser construídoBar
. Um temporárioFoo
será criado, depois vinculado af
, e sua vida útil será prolongada atéf
desaparecer.O programador pode ter pretendido
Bar const& f
e não ter a intenção de fazer uma cópia lá, mas uma cópia é feita independentemente.O exemplo mais comum é o tipo de
*std::map<A,B>::const_iterator
, questd::pair<A const, B> const&
não éstd::pair<A,B> const&
, mas o erro é uma categoria de erros que silenciosamente custam desempenho. Você pode construir astd::pair<A, B>
partir destd::pair<const A, B>
. (A chave em um mapa é const, porque editá-lo é uma má ideia)Tanto o @Barry quanto o @KerrekSB ilustraram esses dois princípios em suas respostas. Isso é simplesmente uma tentativa de destacar as duas questões em uma resposta, com uma redação que visa o problema, em vez de ser centrada no exemplo.
fonte
As três respostas existentes fornecem exemplos em que o uso de
auto
ajuda "torna menos provável a pessiminação involuntária" tornando-o "melhor desempenho".Há um outro lado da moeda. O uso
auto
com objetos que possuem operadores que não retornam o objeto básico pode resultar em código incorreto (ainda compilável e executável). Por exemplo, esta pergunta pergunta como o usoauto
deu resultados diferentes (incorretos) usando a biblioteca Eigen, ou seja , as seguintes linhasresultou em saída diferente. É certo que isso se deve principalmente à avaliação lenta do Eigens, mas esse código é / deve ser transparente para o usuário (da biblioteca).
Embora o desempenho não tenha sido muito afetado aqui, o uso
auto
para evitar pessimização não intencional pode ser classificado como otimização prematura ou, pelo menos, errado;).fonte