Plantilla (C++)

ImprimirCitar

Plantillas son una característica del lenguaje de programación C++ que permite que funciones y clases operen con tipos genéricos. Esto permite que una función o clase funcione en muchos tipos de datos diferentes sin tener que volver a escribir para cada uno.

La biblioteca estándar de C++ proporciona muchas funciones útiles dentro de un marco de plantillas conectadas.

Las principales fuentes de inspiración para las plantillas de C++ fueron los módulos parametrizados proporcionados por CLU y los genéricos proporcionados por Ada.

Resumen técnico

Hay tres tipos de plantillas: plantillas de función, plantillas de clase y, desde C++14, plantillas de variables. Desde C++11, las plantillas pueden ser variables o no variables; en versiones anteriores de C++ siempre son no variables.

Plantillas de funciones

Una plantilla de función se comporta como una función excepto que la plantilla puede tener argumentos de muchos tipos diferentes (ver ejemplo). En otras palabras, una plantilla de función representa una familia de funciones. El formato para declarar plantillas de funciones con parámetros de tipo es:

plantilla.clase Identificador Declaración;plantilla.nombre Identificador Declaración;

Ambas expresiones tienen el mismo significado y se comportan exactamente de la misma manera. La última forma se introdujo para evitar confusiones, ya que un parámetro de tipo no necesita ser una clase hasta C++20. (Puede ser un tipo básico como int o double).

Por ejemplo, la biblioteca estándar de C++ contiene la plantilla de función max(x, y) que devuelve el mayor de x e y. Esa plantilla de función podría definirse así:

plantilla.nombre T T max()T "a, T "b) {} retorno a  b ? a : b; }

Esta definición de función única funciona con muchos tipos de datos. Específicamente, funciona con todos los tipos de datos para los que está definido > (el operador mayor que). El uso de una plantilla de función ahorra espacio en el archivo de código fuente además de limitar los cambios a la descripción de una función y hacer que el código sea más fácil de leer.

Sin embargo, una plantilla no produce un código de objeto más pequeño, en comparación con escribir funciones separadas para todos los diferentes tipos de datos utilizados en un programa específico. Por ejemplo, si un programa usa una versión int y double de la plantilla de función max() que se muestra arriba, el compilador creará una versión de código objeto de max() que opera con argumentos int y otra versión de código objeto que opera con argumentos double. La salida del compilador será idéntica a la que se habría producido si el código fuente hubiera contenido dos versiones separadas sin plantilla de max(), una escrita para manejar int y otra escrito para manejar doble.

Así es como se podría usar la plantilla de función:

#include ■iostreamint principal() {} // Esto llamará a max implicados por deducción de argumentos implícita. std::Cout .. max()3, 7) .. 'n '; // Esto llamará a max garantizadodouble título por deducción de argumentos implícita. std::Cout .. max()3.0, 7.0) .. 'n '; // Necesitamos especificar explícitamente el tipo de argumentos;  // aunque std::type_identity podría resolver este problema... std::Cout .. max.doble()3, 7.0) .. 'n ';}

En los dos primeros casos, el compilador deduce automáticamente que el argumento de la plantilla T es int y double, respectivamente. En el tercer caso, la deducción automática de max(3, 7.0) fallaría porque, en general, el tipo de los parámetros debe coincidir exactamente con los argumentos de la plantilla. Por lo tanto, instanciamos explícitamente la versión double con max<double>().

Esta plantilla de función se puede instanciar con cualquier tipo copiable para el que la expresión y > x es válido. Para los tipos definidos por el usuario, esto implica que el operador mayor que (>) debe estar sobrecargado en el tipo.

Plantillas de clase

Una plantilla de clase proporciona una especificación para generar clases basadas en parámetros. Las plantillas de clase se utilizan generalmente para implementar contenedores. Se crea una instancia de una plantilla de clase pasándole un conjunto dado de tipos como argumentos de plantilla. La biblioteca estándar de C++ contiene muchas plantillas de clases, en particular los contenedores adaptados de la biblioteca de plantillas estándar, como vector.

Plantillas de variables

En C++14, las plantillas también se pueden usar para variables, como en el siguiente ejemplo:

plantilla.nombre T constexpr T pi = T{}3.141592653589793238462643383L}; // (casi) de std:: números::pi

Parámetros de plantilla sin tipo

Aunque la creación de plantillas en tipos, como en los ejemplos anteriores, es la forma más común de creación de plantillas en C++, también es posible crear plantillas en valores. Así, por ejemplo, una clase declarada con

plantilla .int Kclase MyClass;

puede ser instanciado con un int específico.

Como ejemplo del mundo real, el tipo de matriz de tamaño fijo de la biblioteca estándar std::array tiene una plantilla tanto en un tipo (que representa el tipo de objeto que contiene la matriz) como en un número que es de tipo std::size_t (que representa el número de elementos que contiene la matriz). std::array se puede declarar de la siguiente manera:

plantilla.clase T, size_t N struct array;

y se podría declarar una matriz de seis char:

array.char, 6 MyArray;

Especialización de plantilla

Cuando se crea una instancia de una función o clase a partir de una plantilla, el compilador crea una especialización de esa plantilla para el conjunto de argumentos utilizados, y la especialización se denomina especialización generada.

Especialización de plantilla explícita

A veces, el programador puede decidir implementar una versión especial de una función (o clase) para un conjunto determinado de argumentos de tipo plantilla, lo que se denomina especialización explícita. De esta forma, ciertos tipos de plantillas pueden tener una implementación especializada que está optimizada para el tipo o una implementación más significativa que la implementación genérica.

  • Si una plantilla de clase está especializada por un subconjunto de sus parámetros se llama especialización parcial de plantillas (las plantillas de función no pueden ser parcialmente especializadas).
  • Si todos los parámetros son especializados es un completa especialización.

La especialización explícita se usa cuando el comportamiento de una función o clase para elecciones particulares de los parámetros de la plantilla debe desviarse del comportamiento genérico: es decir, del código generado por la plantilla o plantillas principales. Por ejemplo, la siguiente definición de plantilla define una implementación específica de max() para argumentos de tipo const char*:

#include ■cstringplantilla const char* max()const char* a, const char* b) {} // Normalmente, el resultado de una comparación directa // entre dos cadenas C es un comportamiento indefinido; // utilizando std::strcmp se define. retorno std::strcmp()a, b)  0 ? a : b;}

Plantillas variadas

C++11 introdujo plantillas variádicas, que pueden tomar un número variable de argumentos de una manera algo similar a las funciones variádicas como std::printf.

Alias de plantilla

C++11 introdujo alias de plantilla, que actúan como definiciones de tipo parametrizadas.

El siguiente código muestra la definición de un alias de plantilla StrMap. Esto permite, por ejemplo, que StrMap<int> se use como abreviatura de std::unordered_map<int,std::string>.

plantilla.nombre T utilizando StrMap = std::unordered_map.T, std::cuerda;

Características genéricas de programación en otros lenguajes

Al principio, el concepto de plantillas no se incluía en algunos lenguajes, como Java y C# 1.0. La adopción de genéricos por parte de Java imita el comportamiento de las plantillas, pero es técnicamente diferente. C# agregó genéricos (tipos parametrizados) en.NET 2.0. Los genéricos en Ada son anteriores a las plantillas de C++.

Aunque las plantillas de C++, los genéricos de Java y los genéricos de.NET a menudo se consideran similares, los genéricos solo imitan el comportamiento básico de las plantillas de C++. Algunas de las funciones de plantilla avanzadas utilizadas por bibliotecas como Boost y STLSoft, y las implementaciones de STL en sí, para la metaprogramación de plantilla (especialización explícita o parcial, argumentos de plantilla predeterminados, argumentos de plantilla sin tipo, argumentos de plantilla de plantilla,...) son no disponible con genéricos.

En las plantillas de C++, los casos en tiempo de compilación históricamente se realizaban mediante la coincidencia de patrones sobre los argumentos de la plantilla. Por ejemplo, la clase base de la plantilla en el siguiente ejemplo de Factorial se implementa haciendo coincidir 0 en lugar de con una prueba de desigualdad, que anteriormente no estaba disponible. Sin embargo, la llegada a C++11 de funciones de biblioteca estándar como std::conditional ha proporcionado otra forma más flexible de manejar la creación de instancias de plantillas condicionales.

// Inducciónplantilla.no firmado N struct Factorial {} estática constexpr no firmado valor = N * Factorial.N - 1- No.valor;};// Funda base mediante la especialización de plantillas:plantilla struct Factorial.0 {} estática constexpr no firmado valor = 1;};

Con estas definiciones, uno puede calcular, ¡digamos 6! en tiempo de compilación usando la expresión Factorial<6>::value. Alternativamente, constexpr en C++11 / consteval en C++20 se puede usar para calcular dichos valores directamente usando una función en tiempo de compilación. Debido a esto, la metaprogramación de plantillas ahora se usa principalmente para realizar operaciones en tipos.

Contenido relacionado

Particionamiento de disco

Software de identificación

PowerMacintosh

Más resultados...
Tamaño del texto:
Copiar