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)
const
ici et là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.
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:
std
plutôt que comme des éléments intégrés.Par conséquent:
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.
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.
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.
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.
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};
Où intwrapper
est une simple classe wrapper sur un int avec un constructeur trivial prenant un initializer_list
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?