web-dev-qa-db-fra.com

Pourquoi dois-je écrire explicitement le mot clé 'auto'?

Je passe de C++ 98 au C++ 11 et je me suis familiarisé avec le mot clé auto. Je me demandais pourquoi nous devons déclarer explicitement auto si le compilateur est capable de déduire automatiquement le type. Je sais que C++ est un langage fortement typé et qu'il s'agit d'une règle, mais n'était-il pas possible d'obtenir le même résultat sans déclarer explicitement une variable auto?

77
Farsan Rashid

Supprimer le auto explicite casserait le langage:

par exemple.

int main()
{
    int n;
    {
        auto n = 0; // this shadows the outer n.
    }
}

où vous pouvez voir que laisser tomber le auto ne serait pas ombre l'extérieur n.

154
Bathsheba

Votre question permet deux interprétations:

  • Pourquoi avons-nous besoin de "auto"? Ne pouvons-nous pas simplement le laisser tomber?
  • Pourquoi sommes-nous obligés d'utiliser auto? Ne pouvons-nous pas simplement l'avoir implicite, si ce n'est pas donné?

Bathsheba réponse bien la première interprétation, pour la seconde, considérons ce qui suit (en supposant qu'aucune autre déclaration n'existe jusqu'à présent; hypothétiquement valide C++):

int f();
double g();

n = f(); // declares a new variable, type is int;
d = g(); // another new variable, type is double

if(n == d)
{
    n = 7; // reassigns n
    auto d = 2.0; // new d, shadowing the outer one
}

Cela serait possible, les autres langues s'en sortent assez bien avec (enfin, mis à part le problème de l'observation peut-être) ... Ce n'est pas le cas en C++, cependant, et la question (dans le sens de la deuxième interprétation) est maintenant: Pourquoi?

Cette fois, la réponse n’est pas aussi évidente que dans la première interprétation. Une chose est cependant évidente: l'exigence explicite pour le mot clé rend la langue plus sûre (je ne sais pas si c'est ce qui a poussé le comité des langues à prendre sa décision, mais cela reste un point important):

grummel = f();

// ...

if(true)
{
    brummel = f();
  //^ uh, oh, a typo...
}

Pouvons-nous convenir de cela sans avoir besoin d'explications supplémentaires?

Cependant, le plus grand danger de ne pas avoir besoin de auto, c'est que cela signifie que l'ajout d'une variable globale dans un endroit éloigné d'une fonction (par exemple, dans un fichier d'en-tête) peut transformer ce qui était censé être la déclaration d'un localement. variable étendue dans cette fonction dans une affectation à la variable globale ... avec des conséquences potentiellement désastreuses (et certainement très déroutantes).

(cité psmears ' comment en raison de son importance - merci de nous l'avoir laissé entendre)

39
Aconcagua

n'était-il pas possible d'obtenir le même résultat sans déclarer explicitement une variable auto?

Je vais reformuler légèrement votre question de manière à vous aider à comprendre pourquoi vous avez besoin de auto:

N’est-il pas possible d’obtenir le même résultat sans utiliser explicitement un espace réservé de type ?

N'était-ce pas possible ? Bien sûr que c'était "possible". La question est de savoir si cela en vaudrait la peine.

La plupart des syntaxes dans d'autres langues qui ne possèdent pas de noms de types fonctionnent de deux manières. Il y a la manière de Go, où name := value; déclare une variable. Et il y a la façon de faire Python, où name = value; déclare une nouvelle variable si name n'a pas été déclaré auparavant.

Supposons qu'il n'y a pas de problème de syntaxe lors de l'application de la syntaxe à C++ (même si je peux déjà voir que identifier suivi de : en C++ signifie "créer une étiquette"). Alors, que perdez-vous par rapport aux espaces réservés?

Eh bien, je ne peux plus faire ça:

auto &name = get<0>(some_Tuple);

Voir, auto signifie toujours "valeur". Si vous voulez obtenir une référence, vous devez utiliser explicitement un &. Et la compilation échouera à juste titre si l'expression d'affectation est une valeur. Aucune des syntaxes basées sur les affectations ne permet de différencier les références des valeurs.

Vous pouvez maintenant faire en sorte que de telles syntaxes d’affectation déduisent des références si la valeur donnée est une référence. Mais cela voudrait dire que vous ne pouvez pas faire:

auto name = get<0>(some_Tuple);

This copie du tuple, créant un objet indépendant de some_Tuple. Parfois, c'est exactement ce que vous voulez. Ceci est encore plus utile si vous voulez sortir du tuple avec auto name = get<0>(std::move(some_Tuple));.

OK, alors peut-être que nous pourrions étendre un peu ces syntaxes pour prendre en compte cette distinction. Peut-être que &name := value; ou &name = value; voudrait en déduire une référence comme auto&.

OK bien. Et ça:

decltype(auto) name = some_thing();

Oh c'est vrai; C++ a en fait deux espaces réservés: auto et decltype(auto) . L'idée de base de cette déduction est que cela fonctionne exactement comme si vous aviez fait decltype(expr) name = expr;. Donc, dans notre cas, si some_thing() est un objet, il en déduira un objet. Si some_thing() est une référence, il en déduira une référence.

Ceci est très utile lorsque vous travaillez dans un code de modèle et que vous ne savez pas exactement quelle sera la valeur de retour d'une fonction. C'est excellent pour la transmission, et c'est un outil essentiel, même s'il n'est pas largement utilisé.

Nous devons donc maintenant ajouter plus à notre syntaxe. name ::= value; signifie "faire ce que decltype(auto) fait". Je n'ai pas d'équivalent pour la variante Pythonic.

En regardant cette syntaxe, n'est-ce pas assez facile de mal taper accidentellement? Non seulement cela, c'est à peine auto-documenté. Même si vous n'avez jamais vu decltype(auto) auparavant, c'est assez grand et assez évident pour que vous puissiez au moins facilement dire qu'il se passe quelque chose de spécial. Alors que la différence visuelle entre ::= et := est minime.

Mais c'est des trucs d'opinion; il y a plus de problèmes de fond. Vous voyez, tout cela est basé sur l’utilisation de la syntaxe d’affectation. Eh bien ... quid des endroits où vous ne pouvez pas utiliser la syntaxe d’affectation? Comme ça:

for(auto &x : container)

Changeons-nous cela en for(&x := container)? Parce que cela semble dire quelque chose très différent de la plage for. Il semble que ce soit l'instruction d'initialisation d'une boucle for normale, pas une for basée sur une plage. Ce serait aussi une syntaxe différente des cas non déduits.

De même, l'initialisation par copie (en utilisant =) n'est pas la même chose en C++ que l'initialisation directe (en utilisant la syntaxe de constructeur). Donc, name := value; risque de ne pas fonctionner dans les cas où auto name(value) aurait.

Bien sûr, vous pouvez déclarer que := utilisera l'initialisation directe, mais cela serait tout à fait en contradiction avec le comportement du reste de C++.

De plus, il y a encore une chose: C++ 14. Cela nous a donné une caractéristique de déduction utile: la déduction du type de retour. Mais ceci est basé sur des espaces réservés. Tellement comme for basé sur une plage, il est fondamentalement basé sur un nom de type qui est rempli par le compilateur, et non par une syntaxe appliquée à un nom et une expression particuliers.

Vous voyez, tous ces problèmes viennent de la même source: vous êtes en train d'inventer une toute nouvelle syntaxe pour déclarer des variables. Les déclarations d'espace réservé n'ont pas à inventer une nouvelle syntaxe . Ils utilisent exactement la même syntaxe qu'auparavant. ils emploient simplement un nouveau mot clé qui agit comme un type, mais qui a une signification particulière. C’est ce qui lui permet de fonctionner dans la plage for et dans la déduction de type de retour. C'est ce qui lui permet d'avoir plusieurs formes (auto vs. decltype(auto)). Et ainsi de suite.

Les espaces réservés fonctionnent car ils constituent la solution la plus simple au problème, tout en conservant tous les avantages et la généralité de l'utilisation d'un nom de type réel. Si vous proposez une autre solution qui fonctionne aussi universellement que les espaces réservés, il est très peu probable que ce soit aussi simple que des espaces réservés.

À moins qu'il ne s'agisse que d'espaces réservés avec des mots clés ou des symboles différents ...

14
Nicol Bolas

En bref: auto pourrait être supprimé dans certains cas, mais cela entraînerait une incohérence.

Tout d’abord, comme indiqué, la syntaxe de déclaration en C++ est <type> <varname>. Les déclarations explicites nécessitent un type ou au moins un mot clé de déclaration à la place. Nous pourrions donc utiliser var <varname> ou declare <varname> ou quelque chose du genre, mais auto est un mot clé de longue date en C++ et constitue un bon candidat pour le mot clé de déduction de type automatique.

Est-il possible de déclarer implicitement des variables par affectation sans tout casser?

Parfois oui. Vous ne pouvez pas effectuer d'affectation en dehors de fonctions, vous pouvez donc utiliser la syntaxe d'affectation pour les déclarations. Mais une telle approche apporterait une incohérence dans le langage, pouvant éventuellement conduire à des erreurs humaines.

a = 0; // Error. Could be parsed as auto declaration instead.
int main() {
  return 0;
}

Et quand il s'agit de n'importe quel type de variables locales, les déclarations explicites sont un moyen de contrôler la portée d'une variable.

a = 1; // use a variable declared before or outside
auto b = 2; // declare a variable here

Si une syntaxe ambiguë était autorisée, la déclaration de variables globales pourrait convertir soudainement des déclarations implicites locales en assignations. Trouver ces conversions nécessiterait de vérifier tout . Et pour éviter les collisions, vous auriez besoin de noms uniques pour tous les globaux, ce qui détruit en quelque sorte l'idée de cadrage. Donc c'est vraiment mauvais.

12

auto est un mot-clé que vous pouvez utiliser aux endroits où vous devez normalement spécifier un type.

  int x = some_function();

Peut être rendu plus générique en rendant le type int automatiquement déduit:

  auto x = some_function();

C'est donc une extension conservatrice du langage; cela correspond à la syntaxe existante. Sans cela, x = some_function() devient une instruction d'affectation et non plus une déclaration.

11
rustyx

la syntaxe doit être non ambiguë et compatible avec les versions antérieures.

Si auto est supprimé, il n'y aura aucun moyen de distinguer les instructions des définitions.

auto n = 0; // fine
n=0; // statememt, n is undefined.
9
code707

Ajoutant aux réponses précédentes, une note supplémentaire d'un vieux fou: Il semble que vous ayez intérêt à pouvoir commencer à utiliser une nouvelle variable sans la déclarer.

Dans les langues avec la possibilité de définition implicite des variables, cela peut être un gros problème, en particulier dans les systèmes plus grands. Vous faites une faute de frappe et vous déboguez pendant des heures seulement pour découvrir que vous avez involontairement introduit une variable avec une valeur nulle (ou pire) - blue vs bleu, label vs lable ... le résultat est que vous ne pouvez faire confiance à aucun code sans un contrôle approfondi des noms de variables précis.

Le simple fait d'utiliser auto indique au compilateur et au responsable que vous avez l'intention de déclarer une nouvelle variable.

Pensez-y, pour pouvoir éviter ce genre de cauchemars, la déclaration "implicitement aucun" a été introduite dans FORTRAN - et vous la voyez utilisée dans tous les programmes FORTRAN sérieux de nos jours. Ne pas l'avoir, c'est simplement ... effrayant.

3
Bert Bril