¿Por qué no está std::initializer_listincorporado un lenguaje central?
Me parece que es una característica bastante importante de C ++ 11 y, sin embargo, no tiene su propia palabra clave reservada (o algo similar).
En cambio, initializer_listes solo una clase de plantilla de la biblioteca estándar que tiene un mapeo implícito especial de la nueva sintaxis de lista de inicio con {...} llaves que maneja el compilador.
A primera vista, esta solución es bastante peligrosa .
¿Es esta la forma en que se implementarán ahora las nuevas adiciones al lenguaje C ++: por roles implícitos de algunas clases de plantilla y no por el lenguaje central ?
Considere estos ejemplos:
widget<int> w = {1,2,3}; //this is how we want to use a class
por qué se eligió una nueva clase:
widget( std::initializer_list<T> init )
en lugar de usar algo similar a cualquiera de estas ideas:
widget( T[] init, int length ) // (1)
widget( T... init ) // (2)
widget( std::vector<T> init ) // (3)
- una matriz clásica, probablemente podría agregar
constaquí y allá - ya existen tres puntos en el lenguaje (var-args, ahora plantillas variadic), ¿por qué no reutilizar la sintaxis (y hacer que se sienta incorporada )
- solo un contenedor existente, podría agregar
consty&
Todos ellos ya forman parte del idioma. Solo escribí mis 3 primeras ideas, estoy seguro de que hay muchos otros enfoques.
fuente

std::array<T>no es más "parte del lenguaje" questd::initializer_list<T>. Y estos no son los únicos componentes de la biblioteca en los que se basa el lenguaje. Vernew/delete,type_info, diversos tipos de excepción,size_t, etc.const T(*)[N], porque se comporta de manera muy similar a cómostd::initializer_listfunciona.std::arrayo una matriz de tamaño estático son alternativas menos deseables.Respuestas:
Ya había ejemplos de características de lenguaje "principales" que devolvían tipos definidos en el
stdespacio de nombres.typeidregresastd::type_infoy (quizás estirando un punto)sizeofregresastd::size_t.En el primer caso, ya debe incluir un encabezado estándar para poder utilizar esta función denominada "lenguaje principal".
Ahora, para las listas de inicializadores, sucede que no se necesita ninguna palabra clave para generar el objeto, la sintaxis son llaves sensibles al contexto. Aparte de eso, es lo mismo que
type_info. Personalmente, no creo que la ausencia de una palabra clave lo haga "más hacky". Un poco más sorprendente, quizás, pero recuerde que el objetivo era permitir la misma sintaxis de inicializador entre corchetes que ya estaba permitida para los agregados.Entonces sí, probablemente pueda esperar más de este principio de diseño en el futuro:
stdlugar de como incorporados.Por lo tanto:
std.Creo que todo se reduce a que no existe una división absoluta en C ++ entre el "lenguaje central" y las bibliotecas estándar. Son capítulos diferentes en el estándar pero cada uno hace referencia al otro, y siempre ha sido así.
Hay otro enfoque en C ++ 11, que es que las lambdas introducen objetos que tienen tipos anónimos generados por el compilador. Debido a que no tienen nombres, no están en un espacio de nombres en absoluto, ciertamente no en
std. Sin embargo, ese no es un enfoque adecuado para las listas de inicializadores, porque usa el nombre del tipo cuando escribe el constructor que acepta uno.fuente
type_infoysize_tson buenos argumentos ... bueno,size_tes solo un typedef ... así que saltemos esto. La diferencia entretype_infoyinitializer_listes que el primero es el resultado de un operador explícito y el segundo de una acción implícita del compilador. También me parece que esoinitializer_listpodría reemplazarse con algunos contenedores ya existentes ... o mejor aún: ¡cualquiera que el usuario declare como tipo de argumento!vectoreso toma un,arrayentonces podrías construir un vector a partir de cualquier arreglo del tipo correcto, no solo uno generado por la sintaxis de la lista de inicializadores. No estoy seguro de que sería una mala cosa para construir los contenedores de cualquierarray, pero no es la intención de la Comisión en la introducción de la nueva sintaxis.std::arrayni siquiera tiene constructores. Elstd::arraycaso es simplemente inicialización agregada. Además, les doy la bienvenida a unirse a mí en la sala de chat Lounge <C ++>, ya que esta discusión se está volviendo un poco larga.El Comité de Estándares C ++ parece preferir no agregar nuevas palabras clave, probablemente porque eso aumenta el riesgo de romper el código existente (el código heredado podría usar esa palabra clave como el nombre de una variable, una clase o cualquier otra cosa).
Además, me parece que definir
std::initializer_listcomo contenedor con plantilla es una elección bastante elegante: si fuera una palabra clave, ¿cómo accedería a su tipo subyacente? ¿Cómo lo recorrerías? También necesitaría un montón de operadores nuevos, y eso solo lo obligaría a recordar más nombres y más palabras clave para hacer las mismas cosas que puede hacer con los contenedores estándar.Tratar un
std::initializer_listcontenedor como cualquier otro le brinda la oportunidad de escribir código genérico que funcione con cualquiera de esas cosas.ACTUALIZAR:
Para empezar, todos los demás contenedores tienen métodos para agregar, eliminar y colocar elementos, que no son deseables para una colección generada por un compilador. La única excepción es
std::array<>, que envuelve una matriz de estilo C de tamaño fijo y, por lo tanto, sería el único candidato razonable.Sin embargo, como Nicol Bolas señala correctamente en los comentarios, otra diferencia fundamental entre
std::initializer_listy todos los demás contenedores estándar (incluidosstd::array<>) es que estos últimos tienen semántica de valor , mientras questd::initializer_listtiene semántica de referencia . Copiar unstd::initializer_list, por ejemplo, no generará una copia de los elementos que contiene.Además (una vez más, cortesía de Nicol Bolas), tener un contenedor especial para las listas de inicialización de llaves permite sobrecargar la forma en que el usuario realiza la inicialización.
fuente
std::array. Perostd::arrayasigna memoria mientrasstd::initializaer_listenvuelve una matriz en tiempo de compilación. Piense en ello como la diferencia entrechar s[] = "array";ychar *s = "initializer_list";.std::arrayno asigna memoria, es simpleT arr[N];, lo mismo que está respaldandostd::initializer_list.T arr[N]asigna memoria, tal vez no en el montón dinámico sino en otro lugar ... También lo hacestd::array. Sin embargo,initializer_listel usuario no puede construir un no vacío, por lo que obviamente no puede asignar memoria.Esto no es nada nuevo. Por ejemplo, se
for (i : some_container)basa en la existencia de métodos específicos o funciones independientes en lasome_containerclase. C # incluso confía aún más en sus bibliotecas .NET. En realidad, creo que esta es una solución bastante elegante, porque puede hacer que sus clases sean compatibles con algunas estructuras del lenguaje sin complicar la especificación del lenguaje.fuente
beginyendmétodos. Esto es un poco diferente en mi opinión.iterable class MyClass { };initializer_listaunqueDe hecho, esto no es nada nuevo y cuántos han señalado, esta práctica estaba allí en C ++ y está allí, digamos, en C #.
Sin embargo, Andrei Alexandrescu ha mencionado un buen punto sobre esto: puede pensar en ello como parte del espacio de nombres "central" imaginario, entonces tendrá más sentido.
Por lo tanto, en realidad es algo así como:
core::initializer_list,core::size_t,core::begin(),core::end()y así sucesivamente. Esta es solo una desafortunada coincidencia de que elstdespacio de nombres tenga algunas construcciones de lenguaje central en su interior.fuente
No solo puede funcionar completamente en la biblioteca estándar. La inclusión en la biblioteca estándar no significa que el compilador no pueda realizar trucos ingeniosos.
Si bien es posible que no pueda hacerlo en todos los casos, es muy posible que diga: este tipo es bien conocido, o un tipo simple, ignoremos el
initializer_listy solo tengamos una imagen de memoria de cuál debería ser el valor inicializado.En otras palabras,
int i {5};puede ser equivalente aint i(5);, oint i=5;inclusointwrapper iw {5};dóndeintwrapperes un simple contenedor de clase sobre un int con un constructor trivial teniendo unainitializer_listfuente
int i {5}involucra a cualquierastd::initializer_listes incorrecta.intno tiene toma de constructorstd::initializer_list, por lo que5se usa directamente para construirlo. Entonces, el ejemplo principal es irrelevante; simplemente no hay optimización por hacer. Más allá de eso, dado questd::initializer_listimplica que el compilador cree y haga proxy de una matriz 'imaginaria', supongo que puede favorecer la optimización, pero esa es la parte 'mágica' del compilador, por lo que es independiente de si el optimizador en general puede hacer algo inteligente con lo bonito objeto aburrido que contiene 2 iteradores que resultanNo es parte del lenguaje principal porque se puede implementar por completo en la biblioteca, solo línea
operator newyoperator delete. ¿Qué ventaja habría en hacer que los compiladores fueran más complicados de construir?fuente