c++ - enclosed - ¿Por qué std:: initializer_list no es un lenguaje incorporado?
initialize vector c++ 11 (6)
El Comité de estándares de 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_list
como contenedor con plantilla es una elección bastante elegante: si fuera una palabra clave, ¿cómo accedería a su tipo subyacente? ¿Cómo iterarías a través de él? También necesitaría un grupo de operadores nuevos, y eso simplemente 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.
El tratamiento de una std::initializer_list
como cualquier otro contenedor le da la oportunidad de escribir código genérico que funcione con cualquiera de esas cosas.
ACTUALIZAR:
Entonces, ¿por qué introducir un nuevo tipo, en lugar de usar alguna combinación de existente? (de los comentarios)
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 el compilador. La única excepción es std::array<>
, que envuelve una matriz de estilo C de tamaño fijo y, por lo tanto, seguirá siendo el único candidato razonable.
Sin embargo, como señala correctamente Nicol Bolas en los comentarios, otra diferencia fundamental entre std::initializer_list
y todos los demás contenedores estándar (incluido std::array<>
) es que los últimos tienen semántica de valores , mientras que std::initializer_list
tiene semántica de referencia . Copiar una std::initializer_list
, por ejemplo, no causará una copia de los elementos que contiene.
Además (una vez más, cortesía de Nicol Bolas), tener un contenedor especial para listas de inicialización de llaves permite una sobrecarga en la forma en que el usuario está realizando la inicialización.
¿Por qué std::initializer_list
no es un lenguaje de núcleo incorporado?
Me parece que es una característica bastante importante de C ++ 11 y, sin embargo, no tiene su propia palabra clave reservada (o algo por el estilo).
En cambio, initializer_list
es solo una clase de plantilla de la biblioteca estándar que tiene una asignación especial e implícita de la nueva sintaxis de braced-init-list {...}
que maneja el compilador.
Al principio, esta solución es bastante hacky .
¿Es esta la forma en que se implementarán las nuevas adiciones al lenguaje C ++: por roles implícitos de algunas clases de plantillas y no por el lenguaje central ?
Por favor 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ías agregar
const
aquí 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
const
y&
Todos ellos ya son parte del lenguaje. Solo escribí mis 3 primeras ideas, estoy seguro de que hay muchos otros enfoques.
Esto de hecho no es nada nuevo y cuántos han señalado, esta práctica estaba allí en C ++ y está allí, por ejemplo, en C #.
Sin embargo, Andrei Alexandrescu ha mencionado un buen punto al respecto: puede pensar que forma parte del espacio de nombres "núcleo" imaginario, entonces tendrá más sentido.
Por lo tanto, es algo así como: core::initializer_list
, core::size_t
, core::begin()
, core::end()
y así sucesivamente. Esto es solo una desafortunada coincidencia que el std
nombres std
tiene algunas construcciones de lenguaje central dentro de él.
Esto no es nada nuevo. Por ejemplo, for (i : some_container)
basa en la existencia de métodos específicos o funciones independientes en la clase some_container
. C # incluso confía aún más en sus bibliotecas .NET. De hecho, creo que esta es una solución bastante elegante, porque puedes hacer que tus clases sean compatibles con algunas estructuras de lenguaje sin complicar la especificación del lenguaje.
No es parte del lenguaje central porque puede implementarse completamente en la biblioteca, solo operator new
línea operator new
y operator delete
. ¿Qué ventaja habría en hacer compiladores más complicados para construirlo?
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 jugar trucos inteligentes.
Si bien puede no serlo en todos los casos, bien puede decir: este tipo es bien conocido, o un tipo simple, permite ignorar la lista initializer_list
y solo tener una imagen de memoria de lo que debe ser el valor inicializado.
En otras palabras, int i {5};
puede ser equivalente a int i(5);
o int i=5;
o incluso intwrapper iw {5};
Donde intwrapper
es una clase contenedora simple sobre un int con un constructor trivial que toma una initializer_list
Ya había ejemplos de características del lenguaje "central" que devolvían los tipos definidos en el std
nombres std
. typeid
devuelve std::type_info
y (estirando un punto quizás) sizeof
devuelve std::size_t
.
En el primer caso, ya debe incluir un encabezado estándar para poder usar esta característica denominada "core language".
Ahora, para las listas de inicializadores sucede que no se necesita palabra clave para generar el objeto, la sintaxis es llaves rizadas 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, tal vez, pero recuerde que el objetivo era permitir la misma sintaxis de inicializador reforzado que ya estaba permitido para los agregados.
Entonces sí, probablemente puedas esperar más de este principio de diseño en el futuro:
- si surgen más ocasiones en las que es posible introducir nuevas funciones sin nuevas palabras clave, el comité las tomará.
- si las nuevas características requieren tipos complejos, esos tipos se colocarán en
std
en lugar de como builtins.
Por lo tanto:
- si una nueva característica requiere un tipo complejo y puede introducirse sin palabras clave nuevas, obtendrá lo que tiene aquí, que es la sintaxis del "lenguaje central" sin palabras clave nuevas y que utiliza tipos de biblioteca de
std
.
A lo que se reduce, creo, es 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 ningún espacio de nombres, ciertamente no en el std
. Sin embargo, ese no es un enfoque adecuado para las listas de inicializadores, porque se usa el nombre del tipo cuando se escribe el constructor que acepta uno.