Conceptos básicos de la plantilla de C ++: plantillas de funciones

Descargo de responsabilidad: el artículo se inició en febrero, pero, por razones que dependen de mí, no se terminó. El tema es muy extenso, por lo que se publica en forma truncada. Lo que no encaja se considerará más adelante.



Es imposible entender C ++ moderno sin saber qué son los patrones de programación. Esta propiedad del lenguaje abre amplias posibilidades para optimizar y reutilizar el código. En este artículo, trataremos de descubrir qué es y cómo funciona todo.

El mecanismo de plantilla en C ++ nos permite resolver el problema de unificar el algoritmo para varios tipos: no es necesario escribir diferentes funciones para tipos enteros, reales o de usuario; es suficiente compilar un algoritmo generalizado que no depende del tipo de datos, basado solo en propiedades comunes. Por ejemplo, el algoritmo de clasificación puede funcionar con números enteros y con objetos del tipo "automóvil".

Hay plantillas de funciones y plantillas de clase.

Las plantillas de funciones son una descripción general del comportamiento de las funciones que se pueden llamar para objetos de diferentes tipos. En otras palabras, una plantilla de función (función de plantilla, función generalizada) es una familia de funciones diferentes (o una descripción de algoritmo). Por descripción, la plantilla de función es similar a una función regular: la diferencia es que algunos elementos no están definidos (tipos, constantes) y están parametrizados.

Plantillas de clase : una descripción generalizada de un tipo de usuario en la que se pueden parametrizar los atributos y las operaciones de tipo. Son construcciones mediante las cuales se pueden generar clases reales sustituyendo argumentos concretos en lugar de parámetros.

Consideremos las plantillas de funciones con más detalle.

Plantillas de funciones


¿Cómo escribir la primera función de plantilla?


Considere el caso de determinar el elemento mínimo de dos. En el caso de números enteros y números reales, debe escribir 2 funciones.

int _min(int a, int b){ if( a < b){ return a; } return b; } double _min(double a, double b){ if( a < b){ return a; } return b; } 

Por supuesto, puede implementar solo una función, con parámetros válidos, pero para comprender los patrones esto será perjudicial.
¿Qué sucede si se compila la aplicación? Ambas implementaciones de la función caerán en el código binario de la aplicación, incluso si no se usan (sin embargo, ahora los compiladores son muy inteligentes, saben cómo cortar el código no utilizado). ¿Y si necesita agregar una función que defina el mínimo de 2 líneas (es difícil de imaginar sin especificar que hay una línea mínima)?

En este caso, si el algoritmo es común para los tipos con los que tiene que trabajar, puede definir una plantilla de función. El principio, en el caso general, será el siguiente:

  1. se toma una implementación de función para algún tipo;
  2. se asigna la plantilla de encabezado <tipo de clase> (o plantilla <tipo de nombre de tipo>), lo que significa que el algoritmo usa algún tipo de tipo abstracto;
  3. En la implementación de la función, el nombre del tipo se reemplaza por Tipo.

Para la función min, obtenemos lo siguiente:

 template<class Type> Type _min(Type a, Type b){ if( a < b){ return a; } return b; } 

Lo más interesante es el hecho de que si bien no hay una llamada a la función min, cuando se compila, no se crea en código binario (no instanciado ). Y si declara un grupo de llamadas a funciones con variables de varios tipos, para cada compilador creará su propia implementación basada en la plantilla.

Llamar a una función de plantilla generalmente es equivalente a llamar a una función ordinaria. En este caso, el compilador determinará qué tipo usar en lugar de Tipo, en función del tipo de los parámetros reales. Pero si los parámetros sustituidos resultan ser de diferentes tipos, entonces el compilador no podrá generar (instanciar la plantilla) implementación de la plantilla. Entonces, en el siguiente código, el compilador tropezará con la tercera llamada, porque no puede determinar qué Tipo es (¿pensar en por qué?):

 #include <iostream> template<class Type> Type _min(Type a, Type b) { if (a < b) { return a; } return b; } int main(int argc, char** argv) { std::cout << _min(1, 2) << std::endl; std::cout << _min(3.1, 1.2) << std::endl; std::cout << _min(5, 2.1) << std::endl; // oops! return 0; } 

Este problema se resuelve indicando un tipo específico al llamar a una función.

 #include <iostream> template<class Type> Type _min(Type a, Type b) { if (a < b) { return a; } return b; } int main(int argc, char** argv) { std::cout << _min<double>(5, 2.1) << std::endl; return 0; } 

¿Cuándo funcionará la plantilla (no)?


En principio, puede comprender que el compilador simplemente sustituye el tipo deseado en la plantilla. ¿Pero la función resultante siempre será funcional? Obviamente no. Cualquier algoritmo se puede definir independientemente del tipo de datos, pero necesariamente utiliza las propiedades de estos datos. En el caso de la función de plantilla _min, este es un requisito para definir un operador de ordenación (operador <).

Cualquier plantilla de función supone la presencia de ciertas propiedades de un tipo parametrizado, dependiendo de la implementación (por ejemplo, un operador de copia, un operador de comparación, la presencia de un método específico, etc.) En el estándar de lenguaje C ++ esperado, los conceptos serán responsables de esto.

Sobrecarga de plantilla de función


Las plantillas de funciones también se pueden sobrecargar. Por lo general, esta sobrecarga se realiza cuando

 template<class Type> Type* _min(Type* a, Type* b){ if(*a < *b){ return a; } return b; } 

Casos especiales


En algunos casos, la plantilla de función es ineficiente o incorrecta para un tipo particular. En este caso, puede especializar la plantilla, es decir, escribir una implementación para este tipo. Por ejemplo, en el caso de las cadenas, es posible que desee que la función compare solo el número de caracteres. En caso de especialización de la plantilla de función, el tipo para el que se especifica la plantilla no se especifica en el parámetro. El siguiente es un ejemplo de esta especialización.

 template<> std::string _min(std::string a, std::string b){ if(a.size() < b.size()){ return a; } return b; } 

La especialización de la plantilla para tipos específicos se hace nuevamente por razones de economía: si esta versión de la plantilla de función no se usa en el código, entonces no se incluirá en el código binario.
Para el futuro, quedan parámetros múltiples y enteros. Una extensión natural son las plantillas de clase, los conceptos básicos de la programación generativa y el diseño de la biblioteca estándar de C ++. ¡Y un montón de ejemplos!

Source: https://habr.com/ru/post/es436880/


All Articles