Je peux voir pourquoi le auto
type en C ++ 11 améliore l'exactitude et la maintenabilité. J'ai lu que cela peut également améliorer les performances ( presque toujours automatique par Herb Sutter), mais je manque une bonne explication.
- Comment puis
auto
améliorer les performances? - Quelqu'un peut-il donner un exemple?
c++
performance
c++11
auto
DaBrain
la source
la source
Réponses:
auto
peut améliorer les performances en évitant les conversions implicites silencieuses . Un exemple que je trouve convaincant est le suivant.Vous voyez le bug? Nous voici, pensant que nous prenons élégamment chaque élément de la carte par référence const et utilisons la nouvelle expression range-for pour clarifier notre intention, mais en réalité nous copions chaque élément. En effet ,
std::map<Key, Val>::value_type
eststd::pair<const Key, Val>
, nonstd::pair<Key, Val>
. Ainsi, lorsque nous avons (implicitement):Au lieu de prendre une référence à un objet existant et de le laisser là, nous devons faire une conversion de type. Vous êtes autorisé à prendre une référence const à un objet (ou temporaire) d'un type différent tant qu'une conversion implicite est disponible, par exemple:
La conversion de type est une conversion implicite autorisée pour la même raison que vous pouvez convertir un
const Key
en unKey
, mais nous devons construire un temporaire du nouveau type afin de permettre cela. Ainsi, effectivement, notre boucle:(Bien sûr, il n'y a pas réellement d'
__tmp
objet, il est juste là pour l'illustration, en réalité le temporaire sans nom est juste lié àitem
sa durée de vie).Je change simplement en:
vient de nous sauver une tonne de copies - maintenant le type référencé correspond au type d'initialisation, donc aucun temporaire ou conversion n'est nécessaire, nous pouvons simplement faire une référence directe.
la source
std::pair<const Key, Val> const &
comme unstd::pair<Key, Val> const &
? Nouveau sur C ++ 11, je ne sais pas comment le range-for etauto
joue dans cela.auto
cette augmentation des performances. Je vais donc l'écrire dans mes propres mots ci-dessous.auto
améliore les performances". C'est juste un exemple qui "auto
aide à prévenir les erreurs de programmation qui détruisent les performances". Je soutiens qu'il existe une distinction subtile mais importante entre les deux. Pourtant, +1.Parce que
auto
déduit le type de l'expression d'initialisation, aucune conversion de type n'est impliquée. Combiné avec des algorithmes basés sur des modèles, cela signifie que vous pouvez obtenir un calcul plus direct que si vous deviez créer un type vous-même - surtout lorsque vous traitez avec des expressions dont vous ne pouvez pas nommer le type!Un exemple typique vient de (ab) en utilisant
std::function
:Avec
cmp2
etcmp3
, l'ensemble de l'algorithme peut aligner l'appel de comparaison, alors que si vous construisez unstd::function
objet, non seulement l'appel ne peut pas être aligné, mais vous devez également passer par la recherche polymorphe dans l'intérieur effacé du type de l'encapsuleur de fonction.Une autre variante de ce thème est que vous pouvez dire:
Il s'agit toujours d'une référence, liée à la valeur de l'expression d'appel de fonction, et ne construit jamais d'objets supplémentaires. Si vous ne connaissiez pas le type de la valeur renvoyée, vous pourriez être contraint de construire un nouvel objet (peut-être comme temporaire) via quelque chose comme
T && f = MakeAThing()
. (De plus,auto &&
fonctionne même lorsque le type de retour n'est pas mobile et que la valeur de retour est une valeur.)la source
auto
. Votre autre variante est "éviter les copies accidentelles", mais a besoin d'être embellie; pourquoiauto
vous donne-t-il de la vitesse à taper simplement le type là-bas? (Je pense que la réponse est "vous vous trompez de type et il se transforme silencieusement"). Ce qui en fait un exemple moins bien expliqué de la réponse de Barry, non? C'est-à-dire qu'il existe deux cas de base: auto pour éviter l'effacement de type et auto pour éviter les erreurs de type silencieuses qui se convertissent accidentellement, qui ont toutes deux un coût d'exécution.std::bind
,std::function
etstd::stable_partition
ont toutes été intégrées? Ou tout simplement qu'en pratique, aucun compilateur C ++ ne s'alignera de manière suffisamment agressive pour régler le désordre?std::function
constructeur, il sera très complexe de voir à travers l'appel réel, en particulier avec les optimisations de petites fonctions (donc vous ne voulez pas réellement de dévirtualisation). Bien sûr, en principe, tout est comme si ...Il existe deux catégories.
auto
peut éviter l'effacement des caractères. Il existe des types innommables (comme les lambdas) et des types presque innommables (comme le résultat destd::bind
ou d'autres choses comme des modèles d'expression).Sans
auto
, vous finissez par taper effacer les données jusqu'à quelque chose commestd::function
. L'effacement de type a un coût.task1
a une surcharge d'effacement de type - une allocation de tas possible, une difficulté à l'intégrer et une surcharge d'appel de table de fonction virtuelle.task2
n'en a pas. Les Lambdas ont besoin d'une déduction automatique ou d'une autre forme de type pour stocker sans effacement de type; d'autres types peuvent être si complexes qu'ils n'en ont besoin qu'en pratique.Deuxièmement, vous pouvez vous tromper de types. Dans certains cas, le mauvais type fonctionnera parfaitement, mais provoquera une copie.
compilera si
expression()
renvoieBar const&
ouBar
ou mêmeBar&
, d'oùFoo
peut être construitBar
. Un temporaireFoo
sera créé, puis lié àf
, et sa durée de vie sera prolongée jusqu'à ce qu'ilf
disparaisse.Le programmeur a peut-être voulu
Bar const& f
y faire une copie, mais pas l'intention, mais une copie est faite malgré tout.L'exemple le plus courant est le type de
*std::map<A,B>::const_iterator
, ce qui n'eststd::pair<A const, B> const&
pas le casstd::pair<A,B> const&
, mais l'erreur est une catégorie d'erreurs qui coûte silencieusement les performances. Vous pouvez construire un àstd::pair<A, B>
partir d'unstd::pair<const A, B>
. (La clé sur une carte est const, car la modifier est une mauvaise idée)@Barry et @KerrekSB ont tout d'abord illustré ces deux principes dans leurs réponses. Il s'agit simplement d'une tentative de mettre en évidence les deux questions dans une seule réponse, avec une formulation qui vise le problème plutôt que d'être centrée sur l'exemple.
la source
Les trois réponses existantes donnent des exemples où l'utilisation de l'
auto
aide «rend moins susceptible de pessimiser involontairement» ce qui en fait «améliore les performances».Il y a un revers à la médaille. L'utilisation
auto
avec des objets dont les opérateurs ne renvoient pas l'objet de base peut entraîner un code incorrect (toujours compilable et exécutable). Par exemple, cette question demande comment l'utilisation aauto
donné des résultats différents (incorrects) en utilisant la bibliothèque Eigen, c'est -à- dire les lignes suivantesa donné lieu à une sortie différente. Certes, cela est principalement dû à l'évaluation paresseuse d'Eigens, mais ce code est / devrait être transparent pour l'utilisateur (de la bibliothèque).
Bien que les performances
auto
n'aient pas été grandement affectées ici, l'utilisation pour éviter la pessimisation involontaire peut être classée comme une optimisation prématurée, ou du moins erronée;).la source