web-dev-qa-db-fra.com

Existe-t-il un inconvénient à déclarer des variables avec auto en C ++?

Il semble que auto soit une fonctionnalité assez importante à ajouter dans C++ 11 qui semble suivre de nombreux langages plus récents. Comme avec un langage comme Python, je n’ai pas vu de déclaration de variable explicite (je ne sais pas s’il est possible d’utiliser Python standards).

Est-ce qu'il y a un inconvénient à utiliser auto pour déclarer des variables au lieu de les déclarer explicitement?

141
DxAlpha

Vous avez seulement posé des questions sur les inconvénients, alors je vais en souligner certains. Bien utilisé, auto présente également plusieurs avantages. Les inconvénients résultent de la facilité d’abus et du risque accru que le code se comporte de manière inattendue.

Le principal inconvénient est que, en utilisant auto, vous ne connaissez pas nécessairement le type d'objet créé. Il y a aussi des occasions où le programmeur peut s'attendre à ce que le compilateur en déduise un type, alors qu'il en déduit un autre de manière catégorique.

Étant donné une déclaration comme

auto result = CallSomeFunction(x,y,z);

vous n'avez pas nécessairement connaissance de ce type result. Ce pourrait être un int. Ce pourrait être un pointeur. Ce pourrait être autre chose. Toutes ces opérations prennent en charge différentes opérations. Vous pouvez également modifier radicalement le code par une modification mineure telle que

auto result = CallSomeFunction(a,y,z);

car, en fonction des surcharges existantes pour CallSomeFunction(), le type de résultat peut être complètement différent - et le code suivant peut donc se comporter de manière complètement différente de celle prévue. Vous pourriez soudainement déclencher des messages d'erreur dans du code ultérieur (par exemple, essayer ultérieurement de déréférencer un int, en essayant de changer quelque chose qui est maintenant const). Le changement le plus sinistre est le moment où votre changement navigue au-delà du compilateur, mais le code ultérieur se comporte de manière différente et inconnue - éventuellement avec des erreurs.

Ne pas avoir une connaissance explicite du type de certaines variables rend donc plus difficile la justification rigoureuse d'une affirmation selon laquelle le code fonctionne comme prévu. Cela signifie davantage d'efforts pour justifier des affirmations "adaptées à l'usage" dans des domaines de haute criticité (par exemple, critiques pour la sécurité ou critiques).

L’autre inconvénient, plus commun, est la tentation pour un programmeur d’utiliser auto comme un instrument brutal pour forcer le code à la compilation, plutôt que de penser à ce qu’il fait et de s’efforcer de le corriger.

110
Peter

Ce n’est pas un inconvénient de auto de façon exacte, mais concrètement, cela semble être un problème pour certains. De manière générale, certaines personnes: a) considèrent auto comme un sauveur pour les types et s’arrêtent quand on l’utilise, ou b) oublient que auto en déduit toujours des types de valeur. Cela amène les gens à faire des choses comme ça:

auto x = my_obj.method_that_returns_reference();

Oups, nous avons juste copié un objet. C'est souvent un bug ou une performance qui échoue. Ensuite, vous pouvez aussi faire basculer l'inverse:

const auto& stuff = *func_that_returns_unique_ptr();

Maintenant, vous avez une référence en suspens. Ces problèmes ne sont pas du tout causés par auto, je ne les considère donc pas comme des arguments légitimes. Mais il semble que auto rend ces problèmes plus courants (de mon expérience personnelle), pour les raisons que j'ai énumérées au début.

Je pense qu'avec le temps, les gens s'adapteront et comprendront la division du travail: auto en déduit le type sous-jacent, mais vous voulez toujours penser au référentiel et à la constance. Mais cela prend un peu de temps.

75
Nir Friedman

D'autres réponses mentionnent des inconvénients tels que "vous ne savez pas vraiment quel est le type d'une variable." Je dirais que cela est en grande partie lié à la convention de nommage bâclée dans le code. Si vos interfaces sont clairement nommées, vous ne devriez pas avoir besoin de prendre soin du type exact. Bien sûr, auto result = callSomeFunction(a, b); ne vous dit pas grand chose. Mais auto valid = isValid(xmlFile, schema); vous en dit assez pour utiliser valid sans vous soucier de son type exact. Après tout, avec seulement if (callSomeFunction(a, b)), vous ne sauriez pas non plus connaître le type. La même chose avec tout autre objet temporaire de sous-expression. Donc, je ne considère pas cela comme un réel inconvénient de auto.

Je dirais que son principal inconvénient est que, parfois, le type de retour exact est et non ce avec quoi vous voulez travailler. = En effet, le type de retour actuel diffère parfois du type de retour "logique" en tant que détail d'implémentation/optimisation. Les modèles d'expression sont un excellent exemple. Disons que nous avons ceci:

SomeType operator* (const Matrix &lhs, const Vector &rhs);

Logiquement, nous nous attendrions à ce que SomeType soit Vector et nous voulons absolument le traiter comme tel dans notre code. Cependant, il est possible que, pour des raisons d'optimisation, la bibliothèque d'algèbre que nous utilisons implémente des modèles d'expression, et que le type de retour réel est le suivant:

MultExpression<Matrix, Vector> operator* (const Matrix &lhs, const Vector &rhs);

Maintenant, le problème est que MultExpression<Matrix, Vector> va probablement stocker un const Matrix& et const Vector& en interne; il s'attend à ce qu'il se convertisse en Vector avant la fin de son expression complète. Si nous avons ce code, tout va bien:

extern Matrix a, b, c;
extern Vector v;

void compute()
{
  Vector res = a * (b * (c * v));
  // do something with res
}

Cependant, si nous avions utilisé auto ici, nous pourrions avoir des problèmes:

void compute()
{
  auto res = a * (b * (c * v));
  // Oops! Now `res` is referring to temporaries (such as (c * v)) which no longer exist
}
51
Angew

Un des inconvénients est que, parfois, vous ne pouvez pas déclarer const_iterator avec auto. Vous obtiendrez un itérateur ordinaire (non const) dans cet exemple de code extrait de cette question :

map<string,int> usa;
//...init usa
auto city_it = usa.find("New York");
13
ks1322

Cela rend votre code un peu plus difficile ou fastidieux à lire. Imaginez quelque chose comme ça:

auto output = doSomethingWithData(variables);

Maintenant, pour déterminer le type de sortie, vous devez rechercher la signature de la fonction doSomethingWithData.

11
Skam

Comme this développeur, je déteste auto. Ou plutôt, je déteste la façon dont les gens utilisent mal auto.

Je suis d’avis (fort) que auto est destiné à vous aider à écrire du code générique, et non à réduire le typage .
C++ est un langage dont le but est de vous permettre d’écrire du code robuste et non afin de minimiser le temps de développement.
C’est assez évident dans de nombreuses fonctionnalités du C++, mais malheureusement, quelques-unes des plus récentes, comme auto, qui réduisent la dactylographie en induisant les gens en erreur en leur faisant croire qu’ils devraient commencer à être paresseux.

Avant -auto jours, les gens utilisaient typedefs, ce qui était très bien parce que typedef permettait au concepteur de la bibliothèque de vous aider à déterminer le type de retour, de sorte que leur bibliothèque fonctionne comme prévu. Lorsque vous utilisez auto, vous supprimez ce contrôle du concepteur de la classe et demandez au compilateur de déterminer le type de type. qui supprime l’un des plus puissants outils C++ de la boîte à outils et risque rupture leur code.

Généralement, si vous utilisez auto, c'est probablement parce que votre code fonctionne pour tous les types raisonnables , pas parce que vous êtes trop paresseux pour écrire le type avec lequel cela devrait fonctionner. Si vous utilisez auto comme un outil pour aider la paresse, alors vous finirez par introduire des bugs subtils dans votre programme, généralement causé par des conversions implicites qui ne se sont pas produites parce que vous avez utilisé auto.

Malheureusement, ces bugs sont difficiles à illustrer dans un bref exemple ici, car leur brièveté les rend moins convaincants que les exemples concrets qui apparaissent dans un projet utilisateur - cependant, ils apparaissent facilement dans les modèles- code lourd qui attend certaines conversions implicites .

Si vous voulez un exemple, il y en a un ici . Un petit mot cependant: avant d’être tenté de critiquer le code: gardez à l’esprit que de nombreuses bibliothèques bien connues et matures ont été développées autour de telles conversions implicites, et elles sont là parce qu’elles résoudre des problèmes qui peuvent être difficiles sinon impossibles à résoudre autrement. Essayez de trouver un meilleure solution avant de les critiquer.

9
Mehrdad

auto ne présente pas d'inconvénients en soi , et je préconise de l'utiliser (de manière manuelle) dans le nouveau code. Cela permet à votre code de vérifier régulièrement le texte et d'éviter systématiquement le découpage en mode silencieux. (Si B dérive de A et qu'une fonction renvoyant A renvoie soudainement B, alors auto se comporte comme prévu pour stocker sa valeur de retour. )

Bien que le code hérité pré-C++ 11 puisse s’appuyer sur des conversions implicites induites par l’utilisation de variables explicitement typées. Changer une variable de type explicite en auto peut changer le comportement du code, il vaut donc mieux que vous soyez prudent.

6
Laurent LA RIZZA

Le mot clé auto déduit simplement le type de la valeur renvoyée. Par conséquent, il n’est pas équivalent à un objet Python, p. Ex.

# Python
a
a = 10       # OK
a = "10"     # OK
a = ClassA() # OK

// C++
auto a;      // Unable to deduce variable a
auto a = 10; // OK
a = "10";    // Value of const char* can't be assigned to int
a = ClassA{} // Value of ClassA can't be assigned to int
a = 10.0;    // OK, implicit casting warning

Puisque auto est déduit lors de la compilation, cela n’aura aucun inconvénient lors de l’exécution.

4
Leben Asa

Ce que personne n’a mentionné jusqu’ici, mais pour lui-même, mérite une réponse si vous me l’avez demandé.

Depuis (même si tout le monde doit savoir que C != C++) le code écrit en C peut facilement être conçu pour fournir une base pour le code C++ et donc être conçu sans trop d'effort pour être compatible C++, ceci pourrait être une exigence pour la conception.

Je connais certaines règles où des constructions bien définies de C ne sont pas valides pour C++ et vice versa. Mais cela aboutirait simplement à des exécutables cassés et la clause UB connue s'applique, la plupart du temps remarquée par d'étranges boucles générant des plantages ou autre (ou même pouvant rester non détectée, mais cela n'a pas d'importance ici).

Mais auto est la première fois1 ça change!

Imaginez que vous utilisiez auto comme spécificateur de classe de stockage avant de transférer le code. Ce ne serait même pas nécessairement (selon la manière dont il a été utilisé) "casser"; cela pourrait en fait changer le comportement du programme en silence.

C'est une chose à garder à l'esprit.


1Au moins la première fois que je suis au courant.

4
dhein

Comme je l'ai décrit dans cette réponseauto peut parfois entraîner des situations amusantes que vous n'aviez pas prévues. Vous devez explicitement dire auto& avoir un type de référence en ne faisant que auto peut créer un type de pointeur. Cela peut entraîner une confusion en omettant le spécificateur dans son ensemble, ce qui entraîne une copie de la référence au lieu d'une référence réelle.

2
Sombrero Chicken

Une raison à laquelle je peux penser est que vous perdez l’occasion de contraindre la classe qui est renvoyée. Si votre fonction ou méthode renvoyait un long bit 64 et que vous ne vouliez qu'un 32 unsigned int, alors vous perdez la possibilité de contrôler cela.

2
kblansit

Un autre exemple irritant:

for (auto i = 0; i < s.size(); ++i)

génère un avertissement (comparison between signed and unsigned integer expressions [-Wsign-compare]), car i est un entier signé. Pour éviter cela, vous devez écrire, par exemple,.

for (auto i = 0U; i < s.size(); ++i)

ou peut-être mieux:

for (auto i = 0ULL; i < s.size(); ++i)
1
Paul R

Je pense que auto est bon lorsqu'il est utilisé dans un contexte localisé, où le lecteur peut facilement et évidemment déduire son type, ou bien documenté avec un commentaire de ce type ou un nom qui en infère le type réel. Ceux qui ne comprennent pas comment cela fonctionne pourraient le prendre de manière inappropriée, comme l’utiliser à la place de template ou similaire. Voici quelques bons et mauvais cas d'utilisation à mon avis.

void test (const int & a)
{
    // b is not const
    // b is not a reference

    auto b = a;

    // b type is decided by the compiler based on value of a
    // a is int
}

Bonnes utilisations

Itérateurs

std::vector<boost::Tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int> v();

..

std::vector<boost::Tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int>::iterator it = v.begin();

// VS

auto vi = v.begin();

Pointeurs de fonction

int test (ClassWithLongName1 a, ClassWithLongName2 b, int c)
{
    ..
}

..

int (*fp)(ClassWithLongName1, ClassWithLongName2, int) = test;

// VS

auto *f = test;

Mauvaises utilisations

Flux de données

auto input = "";

..

auto output = test(input);

Signature de fonction

auto test (auto a, auto b, auto c)
{
    ..
}

Cas triviaux

for(auto i = 0; i < 100; i++)
{
    ..
}
1
Khaled.K