Je me dirige vers C ++ 11 à partir de C ++ 98 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?

79
Farsan Rashid 23 mai 2018 à 11:15

6 réponses

Meilleure réponse

Supprimer le auto explicite briserait 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 ferait pas ombrer l'extérieur n.

155
Bathsheba 23 mai 2018 à 08:16

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'a-t-il pas été possible d'obtenir le même résultat sans explicitement utiliser un espace réservé de type ?

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

La plupart des syntaxes dans d'autres langages qui ne comportent pas de noms de caractères fonctionnent de deux manières. Il y a la méthode Go-like, où name := value; déclare une variable. Et il y a la manière Python, où name = value; déclare une nouvelle variable si name n'a pas été précédemment déclarée.

Supposons qu'il n'y ait aucun problème de syntaxe avec l'application de l'une ou l'autre syntaxe au 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);

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

Maintenant, vous pouvez faire de telles syntaxes d'affectation déduire 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);

Ceci copie à partir 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 souhaitez vous déplacer du tuple avec auto name = get<0>(std::move(some_tuple));.

OK, alors peut-être pourrions-nous étendre un peu ces syntaxes pour tenir compte de cette distinction. Peut-être que &name := value; ou &name = value; voudrait dire 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 {{ X1}}. L'idée de base de cette déduction est qu'elle 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 le code de modèle et que vous ne savez pas exactement quelle sera la valeur de retour d'une fonction. C'est excellent pour le transfert, et c'est un outil essentiel, même s'il n'est pas largement utilisé.

Alors maintenant, nous devons 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 suffisamment grand et é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 ce sont des trucs d'opinion; il y a plus de problèmes de fond. Voir, tout cela est basé sur l'utilisation de la syntaxe d'affectation. Eh bien ... qu'en est-il 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 de très différent de celui basé sur la plage for. Il semble que ce soit l'instruction d'initialisation d'une boucle for normale, et non une for basée sur une plage. Ce serait également une syntaxe différente des cas non déduits.

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

Bien sûr, vous pouvez déclarer que := utilisera l'initialisation directe, mais ce serait tout à fait en accord avec la façon dont le reste de C ++ se comporte.

De plus, il y a encore une chose: C ++ 14. Cela nous a donné une fonction de déduction utile: la déduction de type de retour. Mais cela est basé sur des espaces réservés. Tout comme le 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 proviennent de la même source: vous inventez une syntaxe entièrement nouvelle pour déclarer des variables. Les déclarations basées sur l'espace réservé n'avaient pas besoin d'inventer une nouvelle syntaxe . Ils utilisent exactement la même syntaxe qu'avant; ils utilisent 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 en fonction de la plage for et pour la déduction du 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 simultanément tous les avantages et la généralité de l'utilisation d'un nom de type réel. Si vous proposez une autre alternative qui fonctionne aussi universellement que les espaces réservés, il est hautement improbable qu'elle soit aussi simple que des espaces réservés.

Sauf s'il s'agissait simplement d'orthographier des espaces réservés avec différents mots-clés ou symboles ...

15
Nicol Bolas 24 mai 2018 à 02:32

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

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 à sa place. Nous pourrions donc utiliser var <varname> ou declare <varname> ou quelque chose comme ça, mais auto est un mot clé de longue date en C ++, et est un bon candidat pour le mot clé de déduction automatique de type.

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 des fonctions, vous pouvez donc utiliser la syntaxe d'affectation pour les déclarations. Mais une telle approche entraînerait une incohérence dans la langue, conduisant éventuellement à des erreurs humaines.

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

Et quand il s'agit de tout type de variables locales, les déclarations explicites sont-elles 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 soudainement convertir des déclarations implicites locales en affectations. Pour trouver ces conversions, il faudrait 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 toute l'idée de portée. Donc c'est vraiment mauvais.

12
Andrew Svietlichnyy 23 mai 2018 à 13:55

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

  int x = some_function();

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

  auto x = some_function();

C'est donc une extension conservatrice de la langue; il s'inscrit dans la syntaxe existante. Sans cela, x = some_function() devient une instruction d'affectation, plus une déclaration.

11
rustyx 23 mai 2018 à 08:28

Ajout aux réponses précédentes, une note supplémentaire d'un vieux pet: il semble que vous pouvez voir cela comme un avantage de pouvoir simplement commencer à utiliser une nouvelle variable sans en aucune façon la déclarer.

Dans les langages avec la possibilité d'une 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 de zéro (ou pire) - blue vs bleu, label vs {{X3} } ... le résultat est que vous ne pouvez vraiment faire confiance à aucun code sans une vérification approfondie des noms de variables précis.

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

Pensez-y, pour être en mesure d'éviter ce genre de cauchemars, la déclaration «aucun implicite» 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 est tout simplement ... effrayant.

3
Bert Bril 30 mai 2018 à 00:03

La syntaxe doit être sans ambiguïté et également compatible avec les versions antérieures.

Si auto est abandonné, il n'y aura aucun moyen de faire la distinction entre les instructions et les définitions.

auto n = 0; // fine
n=0; // statememt, n is undefined.
9
code707 23 mai 2018 à 08:24