stringtranslate.com

Especialización de plantilla parcial

La especialización de plantilla parcial es una forma particular de especialización de plantilla de clase . Generalmente utilizado en referencia al lenguaje de programación C++ , permite al programador especializarse solo en 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 clases. Cuando un programador usa un vector, lo crea una 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. Este proceso se llama monomorfización de genéricos.

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 números enteros, en lugar de multiplicar o dividir por 2), se puede introducir una plantilla de clase especializada con algunos de los parámetros de plantilla preestablecidos. Cuando el compilador ve una plantilla de clase de este tipo instanciada en el 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 (aquella 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 solo permiten especializarse 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 demás genéricos.

Ejemplo

Supongamos que existe una KeyValuePairclase con dos parámetros de plantilla, como se muestra a continuación.

plantilla < clave de nombre de tipo , valor de nombre de tipo > clase KeyValuePair {};      

El siguiente es un ejemplo de una clase que define una especialización de plantilla completa explícita KeyValuePairemparejando números enteros con cadenas. El tipo de clase conserva el mismo nombre que la versión original.

plantilla <> clase KeyValuePair < ​​int , std :: cadena > {};    

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 < clave de nombre de tipo > clase KeyValuePair < ​​clave , std :: cadena > {};     

La siguiente clase de ejemplo KeyStringPairse deriva de la original KeyValuePaircon un nombre nuevo y define una especialización de plantilla parcial. A diferencia de la especialización explícita anterior, solo el parámetro de plantilla Valor de la superclase está especializado, mientras que el parámetro de plantilla Clave sigue siendo genérico.

plantilla < clave de nombre de tipo > clase KeyStringPair : public KeyValuePair < ​​clave , std :: cadena > {};        

No importa qué parámetros de plantilla sean especializados y cuáles sigan siendo genéricos. Por ejemplo, el siguiente también es un ejemplo válido de una especialización parcial de la KeyValuePairclase original.

plantilla < nombre de tipo Valor > clase IntegerValuePair : public KeyValuePair < int , Valor > {};        

Advertencias

Las plantillas de C++ no se limitan a clases: también se pueden utilizar para definir plantillas de funciones . Aunque las plantillas de funciones pueden estar completamente especializadas, no pueden estar parcialmente especializadas, independientemente de si son plantillas de funciones miembro o plantillas de funciones no miembros. Esto puede resultar beneficioso para los escritores de compiladores, pero afecta la flexibilidad y granularidad de lo que pueden hacer los desarrolladores. [1] Sin embargo, las plantillas de funciones se pueden sobrecargar , lo que produce 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 función base plantilla < nombre de tipo ReturnType , nombre de tipo ArgumentType > ReturnType Foo ( ArgumentType arg );      // legal: plantilla de especialización de plantilla de función explícita/completa <> std :: string Foo < std :: string , char > ( char arg ) { return "Full" ; }        // ilegal: especialización parcial de la plantilla de función del tipo de retorno // la especialización parcial de la plantilla de función no está permitida // plantilla <typename ArgumentType> // void Foo<void, ArgumentType>(ArgumentType arg);// legal: sobrecarga la plantilla base para una plantilla de tipo de argumento de puntero < nombre de tipo ReturnType , nombre de tipo ArgumentType > ReturnType Foo ( ArgumentType * argPtr ) { return "PtrOverload" ; }          // legal: nombre de la función base reutilizado. No se considera una sobrecarga. mal formado: declaración no sobrecargable (ver más abajo) plantilla < nombre de tipo ArgumentType > std :: string Foo ( ArgumentType arg ) { return "Return1" ; }        // legal: nombre de la función base reutilizado. No se considera una sobrecarga. mal formado: declaración no sobrecargable (ver más abajo) template < typename ReturnType > ReturnType Foo ( char arg ) { return "Return2" ; }        

En el ejemplo mencionado anteriormente, tenga en cuenta que si bien las dos últimas definiciones de la función Fooson C++ legales, 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 tiene en cuenta el nombre de la función, la lista de tipos de parámetros y el espacio de nombres adjunto (si corresponde). 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 principal ( int argc , char * argv []) { char c = 'c' ; std :: cadena r0 , r1 , r2 , r3 ; // deja que el compilador resuelva la llamada r0 = Foo ( c ); // especifica explícitamente qué función llamar r1 = Foo < std :: string > ( c ); r2 = Foo < std :: cadena , char > ( c ); r3 = Foo < std :: cadena , char > ( & c ); // genera salida std :: cout << r0 << " " << r1 << " " << r2 << " " << r3 << std :: endl ; devolver 0 ; }                                                 //salida esperada: Return1 Return2 Full PtrOverload   

Referencias

  1. ^ Alexandrescu, Andrei (1 de febrero de 2001). Diseño moderno en C++ . Addison Wesley. pag. 23.ISBN​ 0-201-70431-5.
  2. ^ Sutter, Herb (julio de 2001). "¿Por qué no especializarse en plantillas de funciones?". Diario de usuarios de C/C++ . 19 (7) . Consultado el 7 de diciembre de 2014 .
  3. ^ "ISO/IEC JTC1 SC22 WG21 N 3690: Lenguajes de programación - C++" (PDF) . YO ASI. 15 de mayo de 2013. pág. 294 . Consultado el 16 de octubre de 2016 . 13.1 Declaraciones sobrecargables [over.load] No todas las declaraciones de funciones se pueden sobrecargar. Aquí se especifican aquellos que no se pueden sobrecargar. Un programa está mal formado si contiene dos declaraciones no sobrecargables en el mismo ámbito.
  4. ^ "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 sobrecargables [over.load]