La especialización parcial de plantillas es una forma particular de especialización de plantillas de clase . Generalmente utilizada en el 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, en realidad, 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 clase. Cuando un programador utiliza un vector, lo instancia con un tipo de dato 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 una trabajando con un tipo de dato distinto. Este proceso se denomina monomorfización de genéricos.
Si se sabe que una plantilla de clase se utilizará con frecuencia con un tipo de dato específico y que este tipo de dato permite ciertas optimizaciones (por ejemplo, el desplazamiento de bits con enteros, en lugar de la multiplicación o división por 2), se puede introducir una plantilla de clase especializada con algunos parámetros predefinidos. Cuando el compilador detecta dicha plantilla de clase instanciada en el código, generalmente elige la definición de plantilla más especializada que coincide con la instanciación. Por lo tanto, se preferirá una especialización completa explícita (donde se especifican todos los argumentos de la plantilla) a una especialización parcial si todos los argumentos coinciden.
Especialización parcial
Las plantillas pueden tener más de un tipo de parámetro. Algunos compiladores antiguos solo permiten especializar 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 demás genéricos.
Ejemplo
Supongamos que existe una KeyValuePairclase con dos parámetros de plantilla, como se muestra a continuación.
plantilla < typename K , typename V > clase KeyValuePair {};A continuación se muestra un ejemplo de una clase que define una especialización de plantilla completa y explícita KeyValuePairmediante la combinación de números enteros con cadenas de caracteres. El tipo de clase conserva el mismo nombre que la versión original.
usando std :: string ;plantilla <> clase KeyValuePair < int , string > {};El siguiente es un ejemplo de especialización parcial KeyValuePaircon el mismo nombre que la versión original y un parámetro de plantilla especializado.
plantilla < typename K > clase KeyValuePair < K , string > {};La siguiente clase de ejemplo KeyStringPairse deriva de la original KeyValuePaircon un nuevo nombre y define una especialización parcial de la plantilla. A diferencia de la especialización explícita anterior, solo se especializa el Vparámetro de plantilla de la superclaseK , mientras que el parámetro de plantilla permanece genérico.
plantilla < typename K > clase KeyStringPair : público KeyValuePair < K , string > {};No importa qué parámetros de la plantilla se especialicen y cuáles permanezcan genéricos. Por ejemplo, el siguiente es también un ejemplo válido de especialización parcial de la KeyValuePairclase original.
plantilla < typename V > clase IntegerValuePair : público KeyValuePair < int , V > {};Advertencias
Las plantillas de C++ no se limitan a las clases; también se pueden usar para definir plantillas de funciones . Si bien las plantillas de funciones pueden especializarse completamente, no pueden especializarse parcialmente, independientemente de si son plantillas de funciones miembro o no miembro. Esto puede ser beneficioso para los desarrolladores de compiladores, pero afecta la flexibilidad y la granularidad de lo que los desarrolladores pueden hacer. [ 1 ] Sin embargo, las plantillas de funciones pueden sobrecargarse , lo que produce un efecto casi idéntico al de la especialización parcial de plantillas de funciones. [ 2 ] Los siguientes ejemplos ilustran estos puntos.
importar std ;usando std :: string ;// legal: plantilla de función base plantilla < typename Ret , typename Arg > Ret Foo ( Arg arg );// legal: plantilla de especialización de función explícita/completa plantilla <> string Foo < string , char > ( char arg ) { return "Completo" ; }// ilegal: especialización parcial de la plantilla de función del tipo de retorno // no se permite la especialización parcial de la plantilla de función // plantilla <typename Arg> // void Foo<void, Arg>(Arg arg);// legal: sobrecarga la plantilla base para un tipo de argumento de puntero template < typename Ret , typename Arg > Ret Foo ( Arg * argPtr ) { return "PtrOverload" ; }// legal: se reutiliza el nombre de la función base. No se considera una sobrecarga. mal formado: declaración no sobrecargable (ver más abajo) template < typename Arg > string Foo ( Arg arg ) { return "Return1" ; }// legal: se reutiliza el nombre de la función base. No se considera una sobrecarga. mal formado: declaración no sobrecargable (ver más abajo) template < typename Ret > Ret Foo ( char arg ) { return "Return2" ; }En el ejemplo mencionado anteriormente, observe que, si bien las dos últimas definiciones de la función Fooson válidas en C++, se consideran mal formadas según el estándar porque son declaraciones no sobrecargables. [ 3 ] Esto se debe a que la definición de sobrecarga de funciones solo considera el nombre de la función, la lista de tipos de parámetros y el espacio de nombres que la contiene (si lo hay). No considera el tipo de retorno. [ 4 ] Sin embargo, estas funciones aún pueden llamarse indicando explícitamente la firma al compilador, como se demuestra en el siguiente programa.
importar std ;usando std :: string ;int main ( int argc , char * argv []) { char c = 'c' ; // deja que el compilador resuelva la llamada string r0 = Foo ( c ); // especifica explícitamente qué función llamar string r1 = Foo < string > ( c ); string r2 = Foo < string , char > ( c ); string r3 = Foo < string , char > ( & c );// generar salida: // Return1, Return2, Full, PtrOverload std :: println ( "{}, {}, {}, {}" , r0 , r1 , r2 , r3 ); }Referencias
- ^ Alexandrescu, Andrei (1 de febrero de 2001). Diseño moderno en C++ . Addison Wesley. pag. 23.ISBN 0-201-70431-5.
- ↑ Sutter, Herb (julio de 2001). "¿Por qué no especializar las plantillas de funciones?" . C/C++ Users Journal . 19 (7) . Recuperado el 7 de diciembre de 2014 .
- ↑ "ISO/IEC JTC1 SC22 WG21 N 3690: Lenguajes de programación — C++" (PDF) . ISO. 15 de mayo de 2013. pág. 294. Recuperado el 16 de octubre de 2016.
13.1 Declaraciones sobrecargables [over.load] No todas las declaraciones de funciones pueden sobrecargarse. Aquellas que no pueden sobrecargarse se especifican aquí. Un programa está mal formado si contiene dos declaraciones no sobrecargables de este tipo en el mismo ámbito.
- ↑ "ISO/IEC JTC1 SC22 WG21 N 3690: Lenguajes de programación — C++" (PDF) . ISO. 15 de mayo de 2013. págs. 294–296 . Recuperado el 16 de octubre de 2016.
13.1 Declaraciones sobrecargables [over.load]
- C++