web-dev-qa-db-fra.com

Pourquoi std :: initializer_list n'est-il pas un langage intégré?

Pourquoi n'est-ce pas std::initializer_list un langage core intégré?

Il me semble que c'est une fonctionnalité assez importante de C++ 11 et pourtant il n'a pas son propre mot-clé réservé (ou quelque chose de similaire).

Au lieu, initializer_list c'est juste une classe de modèle de la bibliothèque standard qui a un mappage spécial et implicite de la nouvelle liste braced-init-list {...} syntaxe gérée par le compilateur.

À première vue, cette solution est assez hacky .

Est-ce ainsi que les nouveaux ajouts au langage C++ seront désormais implémentés: par rôles implicites de certaines classes de modèles et non par le langage core?


Veuillez considérer ces exemples:

   widget<int> w = {1,2,3}; //this is how we want to use a class

pourquoi une nouvelle classe a-t-elle été choisie:

   widget( std::initializer_list<T> init )

au lieu d'utiliser quelque chose similaire à l'une de ces idées:

   widget( T[] init, int length )  // (1)
   widget( T... init )             // (2)
   widget( std::vector<T> init )   // (3)
  1. un tableau classique, vous pourriez probablement ajouter const ici et là
  2. trois points existent déjà dans le langage (var-args, maintenant des modèles variadiques), pourquoi ne pas réutiliser la syntaxe (et la faire sentir intégrée )
  3. juste un conteneur existant, pourrait ajouter const et &

Tous font déjà partie de la langue. Je n'ai écrit que mes 3 premières idées, je suis sûr qu'il existe beaucoup d'autres approches.

92
emesx

Il y avait déjà des exemples de fonctionnalités de langage "de base" qui renvoyaient des types définis dans l'espace de noms std. typeid renvoie std::type_info et (étirer un point peut-être) sizeof renvoie std::size_t.

Dans le premier cas, vous devez déjà inclure un en-tête standard afin d'utiliser cette fonctionnalité dite de "langage de base".

Maintenant, pour les listes d'initialisation, il arrive qu'aucun mot-clé ne soit nécessaire pour générer l'objet, la syntaxe est des accolades bouclées contextuelles. Mis à part cela, c'est la même chose que type_info. Personnellement, je ne pense pas que l'absence d'un mot-clé le rende "plus hacky". Un peu plus surprenant, peut-être, mais rappelez-vous que l'objectif était de permettre la même syntaxe d'initialisation contreventé qui était déjà autorisée pour les agrégats.

Alors oui, vous pouvez probablement vous attendre à plus de ce principe de conception à l'avenir:

  • si plusieurs occasions se présentent où il est possible d'introduire de nouvelles fonctionnalités sans nouveaux mots-clés, le comité les prendra.
  • si les nouvelles fonctionnalités nécessitent des types complexes, ces types seront placés dans std plutôt que comme des éléments intégrés.

Par conséquent:

  • si une nouvelle fonctionnalité nécessite un type complexe et peut être introduite sans nouveaux mots clés, vous obtiendrez ce que vous avez ici, qui est la syntaxe du "langage de base" sans nouveaux mots clés et qui utilise les types de bibliothèque de std.

Je pense que cela revient à dire qu'il n'y a pas de division absolue en C++ entre le "langage de base" et les bibliothèques standard. Ce sont des chapitres différents de la norme, mais chacun fait référence à l'autre, et il en a toujours été ainsi.

Il existe une autre approche en C++ 11, qui est que les lambdas introduisent des objets qui ont des types anonymes générés par le compilateur. Parce qu'ils n'ont pas de noms, ils ne sont pas du tout dans un espace de noms, certainement pas dans std. Ce n'est pas une approche appropriée pour les listes d'initialisation, car vous utilisez le nom de type lorsque vous écrivez le constructeur qui en accepte un.

47
Steve Jessop

Le comité standard C++ semble préférer ne pas ajouter de nouveaux mots clés, probablement parce que cela augmente le risque de casser le code existant (le code hérité pourrait utiliser ce mot clé comme nom d'une variable, d'une classe ou de tout autre chose).

De plus, il me semble que la définition de std::initializer_list en tant que conteneur basé sur un modèle est un choix assez élégant: s'il s'agissait d'un mot-clé, comment accéderiez-vous à son type sous-jacent? Comment voudriez-vous le parcourir? Vous auriez également besoin d'un tas de nouveaux opérateurs, et cela vous obligerait simplement à vous souvenir de plus de noms et de mots-clés pour faire les mêmes choses que vous pouvez faire avec des conteneurs standard.

Traiter un std::initializer_list comme tout autre conteneur vous donne la possibilité d'écrire du code générique qui fonctionne avec n'importe laquelle de ces choses.

MISE À JOUR:

Alors pourquoi introduire un nouveau type, au lieu d'utiliser une combinaison d'existant? (des commentaires)

Pour commencer, tous les autres conteneurs ont des méthodes pour ajouter, supprimer et placer des éléments, ce qui n'est pas souhaitable pour une collection générée par le compilateur. La seule exception est std::array<>, qui encapsule un tableau de style C de taille fixe et resterait donc le seul candidat raisonnable.

Cependant, comme Nicol Bolas le souligne correctement dans les commentaires, une autre différence fondamentale entre std::initializer_list et tous les autres conteneurs standard (y compris std::array<>) est que ces derniers ont valeur sémantique, tandis que std::initializer_list a référence sémantique. Copie d'un std::initializer_list, par exemple, ne provoquera pas de copie des éléments qu'il contient.

De plus (encore une fois, avec l'aimable autorisation de Nicol Bolas), le fait d'avoir un conteneur spécial pour les listes d'initialisation d'accolade permet de surcharger la façon dont l'utilisateur effectue l'initialisation.

42
Andy Prowl

Ce n'est pas nouveau. Par exemple, for (i : some_container) s'appuie sur existence de méthodes spécifiques ou de fonctions autonomes in some_container classe. C # s'appuie encore plus sur ses bibliothèques .NET. En fait, je pense que c'est une solution assez élégante, car vous pouvez rendre vos classes compatibles avec certaines structures de langage sans compliquer la spécification du langage.

6
Spook

Ce n'est en effet rien de nouveau et comme beaucoup l'ont souligné, cette pratique était là en C++ et est là, disons, en C #.

Andrei Alexandrescu a cependant mentionné un bon point à ce sujet: vous pouvez le considérer comme faisant partie d'un espace de noms "noyau" imaginaire, alors cela aura plus de sens.

Donc, c'est en fait quelque chose comme: core::initializer_list, core::size_t, core::begin(), core::end() et ainsi de suite. C'est juste une coïncidence malheureuse que l'espace de noms std contient des constructions de langage de base à l'intérieur.

4
Artem Tokmakov

Non seulement cela peut fonctionner complètement dans la bibliothèque standard. L'inclusion dans la bibliothèque standard ne signifie pas que le compilateur ne peut pas jouer des tours intelligents.

Bien qu'il ne puisse pas le faire dans tous les cas, il peut très bien dire: ce type est bien connu, ou un type simple, permet d'ignorer le initializer_list Et d'avoir juste une image mémoire de ce que devrait être la valeur initialisée .

En d'autres termes, int i {5}; Peut être équivalent à int i(5); ou int i=5; Ou même intwrapper iw {5};intwrapper est une simple classe wrapper sur un int avec un constructeur trivial prenant un initializer_list

2
Paul de Vrieze

Il ne fait pas partie du langage de base car il peut être implémenté entièrement dans la bibliothèque, juste la ligne operator new et operator delete. Quel avantage y aurait-il à rendre les compilateurs plus compliqués à intégrer?

1
Pete Becker