Dois-je utiliser la nouvelle fonctionnalité "auto" de C ++ 11, en particulier dans les boucles?

20

Quels sont les avantages / inconvénients de l'utilisation du automot - clé, en particulier dans les boucles for?

for(std::vector<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->something();
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); it++ )
{
   it->second->something();
}

for(auto it = x.begin(); it != x.end(); it++ )
{
   it->??
}

On dirait que si vous ne savez pas si vous avez un itérateur pour une carte ou un vecteur vous ne savez pas si vous souhaitez utiliser firstou secondou seulement des propriétés d'accès directement de l'objet, non?

Cela me rappelle le débat C # sur l'opportunité d'utiliser le mot clé var. L'impression que j'ai jusqu'à présent est que dans le monde C ++, les gens sont prêts à adopter le automot - clé avec moins de combat que vardans le monde C #. Pour moi, mon premier instinct est que j'aime connaître le type de la variable afin que je puisse savoir quelles opérations je peux m'attendre à y effectuer.

Utilisateur
la source
3
Attendez! Il y avait un combat sur l'opportunité d'utiliser var? J'ai manqué ça.
pdr
21
Encore mieux, vous pouvez simplement utiliser for (auto& it : x)(ou sans référence si vous voulez copier)
Tamás Szelei
7
Il me semble que si vous écrivez une boucle pour itérer sur le contenu de xet que vous ne savez même pas ce que xc'est, vous ne devriez pas écrire cette boucle en premier lieu ;-)
nikie
@fish: règles pour les boucles basées sur la plage, mais j'aurais été pédant et fait: 'for (T & it: x)' à la place lors de l'utilisation des boucles pour la plage, car je pense que l'utilisation de l'auto est moins informative. Une sorte d'abus d'automobile dans mon esprit.
martiert
La lutte contre l'utilisation de var était un peu idiote, surtout rétrospectivement. Voir la programmation culte du fret .
Craig

Réponses:

38

Les motivations en C ++ sont plus extrêmes, car les types peuvent devenir beaucoup plus compliqués et complexes que les types C # en raison de la métaprogrammation et d'autres choses. autoest plus rapide à écrire et à lire et plus flexible / maintenable qu'un type explicite. Je veux dire, voulez-vous commencer à taper

boost::multi_map<NodeType, indexed_by<ordered_unique<identity<NodeType>>, hashed_non_unique<identity<NodeType>, custom_hasher>>::iterator_type<0> it

Ce n'est même pas le type complet. J'ai raté quelques arguments de modèle.

DeadMG
la source
8
+1 pour l'exemple, mais cela indique également quelque chose sur l'état du C ++ «moderne».
zvrba
22
@zvrba: Ouais - que les installations génériques sont beaucoup plus puissantes que celles de C #.
DeadMG
4
c'est à ça que sert typedef
gbjbaanb
19
@gbjbaanb Non, c'est pour ça auto. Intentionnellement. typedefaide, mais autoaide plus.
Konrad Rudolph
1
typedef invalide l'argument selon lequel "ne pas utiliser l'automobile fait des types vraiment longs"
Michael
28

Dans votre exemple:

for(auto it = x.begin(); it != x.end(); i++)
{
  it->??
}

il doit y avoir une déclaration pour xvisible. Par conséquent, le type de itdoit être évident. Si le type de xn'est pas évident, la méthode est trop longue ou la classe est trop grande.

Kevin Cline
la source
7
En outre, xest un très mauvais nom de variable pour un conteneur. Dans certaines situations, vous pouvez très probablement simplement regarder le nom (sémantiquement valable) et déduire les opérations possibles.
Max
@Max: utilisé uniquement xcomme exemple générique, j'ai tendance à utiliser des noms de variables assez descriptifs.
Utilisateur
@User Bien sûr, je ne pensais pas que c'était un exemple réel;)
Max
14

Objection ! Question chargée.

Pouvez-vous m'expliquer pourquoi le troisième code contient ??, mais pas le premier et le deuxième? Par souci d'équité, votre code doit se lire comme suit:

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->???
}

for(std::map<T>::iterator it = x.begin(); it != x.end(); i++)
{
   it->second->???
}

Là. Même problème même si vous ne l'avez pas utilisé auto.

Et dans tous les cas, la réponse est la même: le contexte compte . Vous ne pouvez pas parler de manière significative d'un morceau de code de manière isolée. Même si vous n'aviez pas utilisé de modèles mais un type concret, cela n'aurait fait que déplacer le problème ailleurs, car le lecteur de votre code devrait connaître la déclaration dudit type.

Si l'utilisation de autodans ces situations rend votre code illisible, vous devez considérer cela comme un signe d'avertissement qu'il y a un problème avec la conception de votre code. Bien sûr, il y a des cas où des détails de bas niveau sont importants (comme pour les opérations sur les bits ou les API héritées), auquel cas un type explicite peut faciliter la lisibilité. Mais en général - non.

En ce qui concerne var(puisque vous l'avez mentionné explicitement), il existe également un vaste consensus dans la communauté C # pour l' utilisation var. Les arguments contre son utilisation reposent généralement sur des erreurs .

Konrad Rudolph
la source
1
Je pense que le point était, avec auto, vous ne savez pas ce que vous mettez ensuite ... est-ce votre code "quelque chose" spécifique ou est-ce un déballage lié au type de données pour arriver à votre objet de données qui a la méthode "quelque chose"
Michael Shaw
1
@Ptolemy Et mon point est: dans les deux autres codes que vous aussi ne connaissez pas ( en général) ce qu'il faut mettre à côté: Test opaque à l'utilisateur comme auto. Pourtant, l'un est censé être bien et l'autre non?! Cela n'a aucun sens. Dans le cas de OP, Test un remplaçant pour un type arbitraire. En vrai code, cela peut être l'utilisation de modèles (for typename std::vector<T>::iterator…)ou d'une interface de classe. Dans les deux cas, le type réel est caché à l'utilisateur, et pourtant nous écrivons régulièrement ce code sans problème.
Konrad Rudolph
1
En fait, oui. Si c'est un vecteur, vous savez que vous devez faire -> et vous avez alors accès à votre type de données. Si c'est une carte, vous savez que vous devez faire -> second-> et puis vous avez accès à votre type de données, si c'est automatique, vous ne savez pas ce que vous devez faire pour accéder à votre type de données. Vous semblez confondre le "quel est le type de données contenu dans la collection STL" avec "quel type de collection STL avons-nous". auto aggrave ce problème.
Michael Shaw
1
@Ptolemy Tous ces arguments sont tout aussi vrais lors de l'utilisation auto. Il est trivial de voir quelles opérations xprennent en charge à partir du contexte. En fait, le type ne vous donne aucune information supplémentaire: dans les deux cas, vous avez besoin d'un secondaire (IDE, documentation, connaissances / mémoire) pour vous indiquer l'ensemble des opérations prises en charge.
Konrad Rudolph
1
Ce @Ptolemy est seulement vrai si vous êtes dans la situation très alambiquée que vous ne savez pas ce que les beginrendements , mais vous ne savez std::vector<>::iteratorc'est. Et vous devez utiliser un mauvais outil de programmation qui ne peut pas vous donner ces informations de manière triviale. C'est très compliqué. En réalité, vous savez soit à la fois beginet iteratorou non plus , et vous devez utiliser un IDE ou un éditeur qui peut rendre facilement les informations pertinentes à votre disposition . Tout éditeur IDE et de programmation moderne peut le faire.
Konrad Rudolph
11

PRO

Votre code :

for(std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

ne va pas compiler, en raison du nom dépendant du modèle.

Voici la syntaxe correcte:

for( typename std::vector<T>::iterator it = x.begin(); it != x.end(); i++)

Regardez maintenant la longueur de la déclaration de type. Cela explique pourquoi le automot clé a été introduit. Cette :

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

est plus concis. Donc, c'est un pro.


CON

Vous devez être un peu prudent. Avec le mot-clé auto, vous obtenez le type que vous avez déclaré.

Par exemple :

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto it : v )
{
  ++ it;   // ops modifying copies of vector's elements
}

contre

std::vector< int > v{ 1, 2, 3, 4 };
for ( auto & it : v )   // mind the reference
{
  ++ it;   // ok, vector's elements modified
}

Pour conclure: oui, vous devriez le faire, mais pas en abuser. Certaines personnes ont tendance à trop l'utiliser et à mettre l'auto partout, comme dans l'exemple suivant:

auto i = 0;

contre

int i = 0;
BЈовић
la source
auto i = 0. Coupable. Je fais ça. Mais c'est parce que je sais que 0c'est un type littéral int. (et une constante octale ;-))
Laurent LA RIZZA
6

Oui tu devrais! auton'efface pas le type; même si vous "ne savez pas" ce qui x.begin()est, le compilateur sait et signalera une erreur si vous essayez d'utiliser le type incorrectement. De plus, il n'est pas inhabituel d'émuler mapavec un vector<pair<Key,Value>>, donc le code utilisant autofonctionnera pour les deux représentations de dictionnaire.

zvrba
la source
4

Oui, vous devez utiliser autocomme règle par défaut. Il présente des avantages bruts par rapport à la spécification explicite du type:

  • Cela ne vous oblige pas à taper des choses que le compilateur connaît déjà.
  • Il fait que le type de variables "suit" tout changement dans les types de valeur de retour.
  • Ce faisant, il empêche l' introduction silencieuse de conversions implicites et de découpage dans l'initialisation de variable locale.
  • Il supprime la nécessité de certains calculs de type explicites dans les modèles.
  • Il supprime le besoin de nommer les types de retour avec des noms longs. (ceux que vous copiez-collez à partir des diagnostics du compilateur)

C'est là que vous avez le choix. Il y a aussi des cas où vous n'avez pas le choix:

  • Il permet la déclaration de variables de types inexprimables, comme le type d'un lambda.

Pourvu que vous sachiez exactement ce autoque cela fait, cela n'a aucun inconvénient.

Laurent LA RIZZA
la source