
Resumen de partes anteriores
Debido a las restricciones en la capacidad de usar compiladores de C ++ 11, y por la falta de alternativa, boost quería escribir su propia implementación de la biblioteca estándar de C ++ 11 sobre la biblioteca de C ++ 98 / C ++ 03 suministrada con el compilador.
Se implementaron
Static_assert ,
noexcept ,
countof , y también, después de considerar todas las características de compilador y definiciones no estándar, apareció información sobre la funcionalidad que es compatible con el compilador actual. Se
incluye su propia implementación de
nullptr , que se selecciona en la etapa de compilación.
Ha llegado el momento de
type_traits y toda esta "plantilla mágica especial".
Enlace a GitHub con el resultado de hoy para impacientes y no lectores:
Los compromisos y las críticas constructivas son bienvenidos
Sumérgete en el mundo de la "plantilla mágica" C ++.
Tabla de contenidos
IntroduccionCapítulo 1. Viam supervadet vadensCapítulo 2. #ifndef __CPP11_SUPPORT__ #define __COMPILER_SPECIFIC_BUILT_IN_AND_MACRO_HELL__ #endifCapítulo 3. Encontrar la implementación nullptr perfectaCapítulo 4. Magia de plantilla de C ++....
4.1 Comenzamos pequeño....
4.2 Acerca de cuántos errores milagrosos compila el registro para nosotros....
4.3 Punteros y todo-todo-todo....
4.4 ¿Qué más se necesita para la biblioteca de plantillas?Capitulo 5
...
Capítulo 4. Magia de plantilla de C ++
Después de terminar con las palabras clave de C ++ 11 y todos los "interruptores" dependientes de la definición entre sus implementaciones, comencé a llenar
type_traits . En verdad, ya tenía bastantes clases de plantillas, similares a las estándar, que ya habían trabajado en proyectos durante mucho tiempo y, por lo tanto, me quedaba por poner todo esto en la misma forma, así como agregar la funcionalidad que faltaba.

Honestamente, me inspira la programación de plantillas. Especialmente la comprensión de que todo esto es una variedad de opciones: cálculos, ramificación de código, condiciones, verificación de errores se realiza durante el proceso de compilación y nada cuesta el programa final en tiempo de ejecución. Y dado que las plantillas en C ++ son esencialmente un
lenguaje de programación completo de Turing , anticipé cuán elegante y relativamente fácil sería posible implementar la parte del estándar relacionada con la programación en plantillas. Pero, para destruir inmediatamente todas las ilusiones, diré que toda la teoría de la integridad de Turing se divide en implementaciones concretas de plantillas en compiladores. Y esta parte de escribir la biblioteca, en lugar de soluciones elegantes y "trucos" de programación de plantillas, se convirtió en una lucha feroz con los compiladores, mientras que cada uno "colapsó" a su manera, y es bueno si entró en un error interno del compilador, o incluso se estrelló fuertemente con excepciones no manejadas. Lo mejor de todo se mostraron GCC (g ++), que estoicamente "masticación" todos los diseños de la plantilla y sólo se juró (en el caso) en áreas donde la necesidad de
nombre de tipo explícito.4.1 Comenzando pequeño
Comencé con plantillas simples para
std :: integral_constant ,
std :: bool_constant y pequeñas plantillas similares.
template<class _Tp, _Tp Val> struct integral_constant {
Basado en
condicional, puede ingresar plantillas convenientes para operaciones lógicas {"y", "o", "no"} sobre tipos (¡y todas estas operaciones se consideran correctas en la etapa de compilación! Es genial, ¿no?):
namespace detail { struct void_type {};
Tres puntos merecen atención aquí:
1) Es importante poner siempre un espacio entre los corchetes angulares ('<' y '>') de las plantillas, ya que antes de C ++ 11 no había ninguna aclaración en el estándar sobre cómo interpretar '>>' y '<<' en código como _o _ <_ B2, _o _ <_ B3, _B4 >> , y por lo tanto, casi todos los compiladores trataron esto como un operador de cambio de bit, lo que conduce a un error de compilación.
2) En algunos compiladores (Visual Studio 6.0, por ejemplo) hubo un error que consistió en el hecho de que era imposible usar el tipo vacío como parámetro de plantilla. Para estos fines, se introduce un tipo void_type separado en el pasaje anterior para reemplazar el tipo void donde se requiere el valor del parámetro de plantilla predeterminado.
3) Los compiladores muy antiguos (Borland C ++ Builder, por ejemplo) tenían un tipo bool implementado de forma torcida, que en algunas situaciones "repentinamente" se convirtió en int ( verdadero -> 1, falso -> 0), así como tipos de variables estáticas constantes del tipo bool (y no solo ellos), si estuvieran contenidos en clases de plantilla. Debido a todo este lío, como resultado, para una comparación completamente inofensiva en el estilo de my_template_type :: static_bool_value == false, el compilador podría emitir fácilmente un encantamiento que no puede lanzar 'tipo indefinido' a int (0) o algo así. Por lo tanto, es necesario tratar siempre de indicar explícitamente el tipo de valores para la comparación, ayudando así al compilador a determinar con qué tipos se trata.
Agregue más trabajo con valores
constantes y
volátiles . Primero, el
remove_ ... trivialmente implementado donde simplemente especializamos la plantilla para ciertos modificadores de tipo: si el tipo con el modificador entra en la plantilla, el compilador debe, después de mirar todas las especializaciones (recuerde el principio SFINAE del
capítulo anterior ) de la plantilla, seleccionar la más adecuada (con indicación explícita del modificador deseado) :
template<class _Tp> struct is_function; template<class _Tp> struct remove_const {
Y luego implementamos plantillas
add_ ... donde todo ya es un poco más complicado:
namespace detail { template<class _Tp, bool _IsFunction> struct _add_const_helper { typedef _Tp const type; }; template<class _Tp> struct _add_const_helper<_Tp, true> { typedef _Tp type; }; template<class _Tp, bool _IsFunction> struct _add_volatile_helper { typedef _Tp volatile type; }; template<class _Tp> struct _add_volatile_helper<_Tp, true> { typedef _Tp type; }; template<class _Tp, bool _IsFunction> struct _add_cv_helper { typedef _Tp const volatile type; }; template<class _Tp> struct _add_cv_helper<_Tp, true> { typedef _Tp type; }; }
Aquí procesamos cuidadosamente los tipos de referencia por separado para no perder el enlace. Además, no nos olvidaremos de los tipos de funciones que es imposible hacer
volátiles o
constantes en principio, por lo tanto, los dejaremos "tal cual". Puedo decir que todo esto parece muy simple, pero este es exactamente el caso cuando "el demonio está en los detalles", o mejor dicho, "los errores están en los detalles de la implementación".
El final de la primera parte del cuarto capítulo. En la
segunda parte , hablaré sobre cuán difícil se da la programación de plantillas al compilador, y también habrá una magia de plantillas más genial. Ah, y sin embargo, por qué es
largo, no es una
constante integral, según algunos compiladores hasta el día de hoy.
Gracias por su atencion