La especialización parcial en plantillas es una forma particular de especialización en plantillas de clases . Usualmente usado en referencia al lenguaje de programación C ++ , permite al programador especializar solo algunos argumentos de una plantilla de clase, a diferencia de la especialización completa explícita, donde se proporcionan todos los argumentos de la plantilla.
Plantillas y especialización
Las plantillas de clase son realmente metaclases: son tipos de datos abstractos parciales que proporcionan instrucciones al compilador sobre cómo crear clases con los miembros de datos adecuados. Por ejemplo, los contenedores estándar de C ++ son plantillas de clases. Cuando un programador usa un vector, lo instancia con un tipo de datos específico, por ejemplo, int, string o double. Cada tipo de vector da como resultado una clase diferente en el código objeto del compilador, cada uno trabajando con un tipo de datos diferente.
Si se sabe que una plantilla de clase se utilizará con un tipo de datos específico con bastante frecuencia y este tipo de datos permite algunas optimizaciones (por ejemplo, desplazamiento de bits con enteros, en lugar de multiplicar o dividir por 2), se puede introducir una plantilla de clase especializada con algunos de los parámetros de la plantilla preestablecidos. Cuando el compilador ve una plantilla de clase de este tipo instanciada en código, generalmente elegirá la definición de plantilla más especializada que coincida con la instanciación. Por lo tanto, se preferirá una especialización completa explícita (una en la que se especifican todos los argumentos de la plantilla) a una especialización parcial si todos los argumentos de la plantilla coinciden.
Especialización parcial
Las plantillas pueden tener más de un tipo de parámetro. Algunos compiladores más antiguos permiten que uno solo se especialice en todos o ninguno de los parámetros de la plantilla. Los compiladores que admiten la especialización parcial permiten al programador especializar algunos parámetros y dejar los otros genéricos.
Ejemplo
Suponga que existe una KeyValuePair
clase con dos parámetros de plantilla, como sigue.
plantilla < typename Key , typename Value > class KeyValuePair {};
El siguiente es un ejemplo de una clase que define una especialización de plantilla completa explícita KeyValuePair
emparejando enteros con cadenas. El tipo de clase conserva el mismo nombre que la versión original.
plantilla <> clase KeyValuePair < int , std :: string > {};
El siguiente es un ejemplo de especialización parcial de KeyValuePair
con el mismo nombre que la versión original y un parámetro de plantilla especializado.
plantilla < typename Key > class KeyValuePair < Key , std :: string > {};
La siguiente clase de ejemplo KeyStringPair
se deriva del original KeyValuePair
con un nuevo nombre y define una especialización de plantilla parcial. A diferencia de la especialización explícita anterior, solo el parámetro de plantilla de valor de la superclase está especializado, mientras que el parámetro de plantilla de clave sigue siendo genérico.
plantilla < typename Key > clase KeyStringPair : public KeyValuePair < Key , std :: string > {};
No importa qué parámetros de la plantilla estén especializados y cuáles sean genéricos. Por ejemplo, el siguiente es también un ejemplo válido de una especialización parcial de la KeyValuePair
clase original .
plantilla < typename Value > clase IntegerValuePair : public KeyValuePair < int , Value > {};
Advertencias
Las plantillas de C ++ no se limitan a las clases; también se pueden usar para definir plantillas de funciones . Aunque las plantillas de funciones pueden ser completamente especializadas, no pueden ser parcialmente especializadas, independientemente de si son plantillas de función miembro o plantillas de función no miembro. Esto puede ser beneficioso para los escritores de compiladores, pero afecta la flexibilidad y granularidad de lo que pueden hacer los desarrolladores. [1] Pero, las plantillas de funciones se pueden sobrecargar , lo que da casi el mismo efecto que tendría la especialización parcial de plantillas de funciones. [2] Los siguientes ejemplos se proporcionan para ilustrar estos puntos.
// legal: plantilla de plantilla de función base < typename ReturnType , typename ArgumentType > ReturnType Foo ( ArgumentType arg );// legal: explícita / lleno función de plantilla especialización plantilla <> std :: string Foo < std :: string , Char > ( Char arg ) { retorno "completa" ; }// ilegal: plantilla de función parcial especialización del tipo de retorno // plantilla de función no se permite la especialización parcial // plantilla // void Foo (ArgumentType arg); ,>// legal: sobrecarga la plantilla base para una plantilla de tipo de argumento de puntero < typename ReturnType , typename ArgumentType > ReturnType Foo ( ArgumentType * argPtr ) { return "PtrOverload" ; }// legal: nombre de función base reutilizado. No se considera una sobrecarga. mal formado: plantilla de declaración no sobrecargable (ver más abajo) < typename ArgumentType > std :: string Foo ( ArgumentType arg ) { return "Return1" ; }// legal: nombre de función base reutilizado. No se considera una sobrecarga. mal formado: plantilla de declaración no sobrecargable (ver más abajo) < typename ReturnType > ReturnType Foo ( char arg ) { return "Return2" ; }
En el ejemplo enumerado anteriormente, tenga en cuenta que, si bien las dos últimas definiciones de la función Foo
son C ++ legales, se consideran mal formadas según el estándar porque son declaraciones que no se pueden sobrecargar. [3] Esto se debe a que la definición de sobrecarga de funciones solo tiene en cuenta el nombre de la función, la lista de tipos de parámetros y el espacio de nombres adjunto (si lo hay). No tiene en cuenta el tipo de devolución. [4] Sin embargo, estas funciones aún se pueden llamar indicando explícitamente la firma al compilador, como lo demuestra el siguiente programa.
// nota: se compilará junto con las definiciones de Foo anterioresint main ( int argc , char * argv []) { char c = 'c' ; std :: cadena r0 , r1 , r2 , r3 ; // deja que el compilador resuelva la llamada r0 = Foo ( c ); // especificar explícitamente a qué función llamar r1 = Foo < std :: string > ( c ); r2 = Foo < estándar :: cadena , carácter > ( c ); r3 = Foo < std :: string , char > ( & c ); // generar salida std :: cout << r0 << "" << r1 << "" << r2 << "" << r3 << std :: endl ; return 0 ; }// resultado esperado: Return1 Return2 Full PtrOverload
Referencias
- ^ Alexandrescu, Andrei (1 de febrero de 2001). Diseño C ++ moderno . Addison Wesley. pag. 23. ISBN 0-201-70431-5.
- ^ Sutter, Herb (julio de 2001). "¿Por qué no especializar las plantillas de funciones?" . Diario de usuarios de C / C ++ . 19 (7) . Consultado el 7 de diciembre de 2014 .
- ^ "ISO / IEC JTC1 SC22 WG21 N 3690: Lenguajes de programación - C ++" (PDF) . YO ASI. 15 de mayo de 2013. p. 294 . Consultado el 16 de octubre de 2016 .
13.1 Declaraciones sobrecargadas [over.load] No todas las declaraciones de funciones pueden sobrecargarse. Aquellos que no se pueden sobrecargar se especifican aquí. Un programa está mal formado si contiene dos declaraciones no sobrecargadas en el mismo ámbito.
- ^ "ISO / IEC JTC1 SC22 WG21 N 3690: Lenguajes de programación - C ++" (PDF) . YO ASI. 15 de mayo de 2013. págs. 294–296 . Consultado el 16 de octubre de 2016 .
13.1 Declaraciones para sobrecargar [over.load]