Pourquoi std::initializer_list
un langage de base n'est-il pas 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 de cela, initializer_list
il s'agit simplement d' une classe de modèle de la bibliothèque standard qui a un mappage implicite spécial de la nouvelle syntaxe braced-init-list {...}
qui est gérée par le compilateur.
À première vue, cette solution est assez piratée .
Est-ce ainsi que les nouveaux ajouts au langage C ++ seront désormais implémentés: par les rôles implicites de certaines classes de modèles et non par le langage de base ?
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 de similaire à l'une de ces idées:
widget( T[] init, int length ) // (1)
widget( T... init ) // (2)
widget( std::vector<T> init ) // (3)
- un tableau classique, vous pourriez probablement ajouter
const
ici et là - 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 )
- 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 de nombreuses autres approches.
la source
std::array<T>
ne fait pas plus partie de la langue questd::initializer_list<T>
. Et ce ne sont pas les seuls composants de bibliothèque sur lesquels le langage s'appuie. Voirnew
/delete
,type_info
, divers types d'exceptions,size_t
etc.const T(*)[N]
, parce que cela se comporte de manière très similaire à la façon dontstd::initializer_list
fonctionne.std::array
ou un tableau de taille statique sont des alternatives moins souhaitables.Réponses:
Il y avait déjà des exemples de fonctionnalités de langage «de base» qui renvoyaient des types définis dans l'
std
espace de noms.typeid
retournestd::type_info
et (étirant peut-être un point)sizeof
retournestd::size_t
.Dans le premier cas, vous devez déjà inclure un en-tête standard pour utiliser cette fonctionnalité dite de "langage de base".
Désormais, pour les listes d'initialiseurs, il arrive qu'aucun mot clé ne soit nécessaire pour générer l'objet, la syntaxe étant des accolades sensibles au contexte. En dehors de cela, c'est la même chose que
type_info
. Personnellement, je ne pense pas que l'absence de mot-clé le rend "plus hacky". Un peu plus surprenant, peut-être, mais rappelez-vous que l'objectif était d'autoriser la même syntaxe d'initialisation entre accolades que celle 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
.Ce à quoi cela revient, je pense, c'est 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 dans la norme mais chacun fait référence à l'autre, et il en a toujours été ainsi.
Il existe une autre approche dans C ++ 11, à savoir que les lambdas introduisent des objets qui ont des types anonymes générés par le compilateur. Parce qu'ils n'ont aucun nom, ils ne sont pas du tout dans un espace de noms, certainement pas
std
. Ce n'est pas une approche appropriée pour les listes d'initialiseurs, cependant, car vous utilisez le nom de type lorsque vous écrivez le constructeur qui en accepte un.la source
type_info
etsize_t
sont de bons arguments .. eh biensize_t
c'est juste un typedef .. alors sautons ceci. La différence entretype_info
etinitializer_list
est que le premier est le résultat d'un opérateur explicite et le second d'une action implicite du compilateur. Il me semble aussi que celainitializer_list
pourrait être remplacé par certains conteneurs déjà existants ... ou encore mieux: tout ce que l'utilisateur déclare comme type d'argument!vector
cela prend unarray
alors vous pouvez construire un vecteur à partir de n'importe quel tableau du bon type, et pas seulement de celui généré par la syntaxe de la liste d'initialisation. Je ne suis pas sûr que ce serait une mauvaise chose de construire des conteneurs à partir de n'importe lequelarray
, mais ce n'est pas l'intention du comité en introduisant la nouvelle syntaxe.std::array
n'a même pas de constructeur. Lestd::array
cas est simplement une initialisation agrégée. Aussi, je vous souhaite la bienvenue à me rejoindre dans la salle de discussion Lounge <C ++>, car cette discussion devient un peu longue.Le Comité des normes C ++ semble préférer ne pas ajouter de nouveaux mots-clés, probablement parce que cela augmente le risque de briser le code existant (le code hérité pourrait utiliser ce mot-clé comme nom d'une variable, d'une classe ou autre).
De plus, il me semble que se définir
std::initializer_list
comme un 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 le feriez-vous? Vous auriez également besoin d'un tas de nouveaux opérateurs, ce qui 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 n'importe quel autre conteneur vous donne la possibilité d'écrire du code générique qui fonctionne avec l'une de ces choses.METTRE À JOUR:
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 souligne à juste titre dans les commentaires, une autre, la différence fondamentale entre
std::initializer_list
et tous les autres conteneurs standard (y comprisstd::array<>
) est que celui - ci les ont sémantique de valeur , toutstd::initializer_list
a la sémantique de référence . La copie d'unstd::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'accolades permet de surcharger la manière dont l'utilisateur effectue l'initialisation.
la source
std::array
. Maisstd::array
alloue de la mémoire tout enstd::initializaer_list
enveloppant un tableau au moment de la compilation. Pensez-y comme la différence entrechar s[] = "array";
etchar *s = "initializer_list";
.std::array
n'alloue pas de mémoire, c'est un simpleT arr[N];
, la même chose que le supportstd::initializer_list
.T arr[N]
n'alloue la mémoire, peut - être pas dans le tas dynamique , mais ailleurs ... Ainsi faitstd::array
. Cependant, un non-videinitializer_list
ne peut pas être construit par l'utilisateur donc il ne peut évidemment pas allouer de mémoire.Cela n’a rien de nouveau. Par exemple,
for (i : some_container)
repose sur l' existence de méthodes spécifiques ou de fonctions autonomes dans lasome_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.la source
begin
etend
méthodes. C'est un peu différent de l'OMI.iterable class MyClass { };
initializer_list
cependantCe n'est en effet rien de nouveau et combien 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 une partie de l'espace de noms imaginaire "de base", alors cela aura plus de sens.
Donc, il est en fait quelque chose comme:
core::initializer_list
,core::size_t
,core::begin()
,core::end()
et ainsi de suite. C'est juste une malheureuse coïncidence que l'std
espace de noms contient des constructions de langage de base.la source
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, ignorons le
initializer_list
et avons juste une image mémoire de ce que devrait être la valeur initialisée.En d'autres termes, cela
int i {5};
peut être équivalent àint i(5);
ouint i=5;
ou mêmeintwrapper iw {5};
Whereintwrapper
est une classe wrapper simple sur un int avec un constructeur trivial prenant uninitializer_list
la source
int i {5}
impliquestd::initializer_list
est fausse.int
n'a pas de constructeurstd::initializer_list
, donc le5
est juste utilisé directement pour le construire. Ainsi, l'exemple principal n'est pas pertinent; il n'y a tout simplement aucune optimisation à faire. Au-delà de cela, étant donné questd::initializer_list
le compilateur crée et envoie par proxy un tableau `` imaginaire '', je suppose que cela peut favoriser l'optimisation, mais c'est la partie `` magique '' du compilateur, donc c'est différent de savoir si l'optimiseur en général peut faire quelque chose d'intelligent avec le joli objet terne contenant 2 itérateurs qui en résulteCela ne fait pas partie du langage de base car il peut être implémenté entièrement dans la bibliothèque, il suffit de la ligne
operator new
etoperator delete
. Quel avantage y aurait-il à rendre les compilateurs plus compliqués à intégrer?la source