C ++

Cómo utilizar plantillas de C ++

Cómo utilizar plantillas de C ++

Introducción

En la programación básica de C ++, el tipo de datos, e.gramo., int o char, debe indicarse en una declaración o una definición. Un valor como 4 o 22 o -5 es un int. Un valor como 'A' o 'b' o 'c' es un carácter. El mecanismo de plantilla permite al programador usar un tipo genérico para un conjunto de tipos reales. Por ejemplo, el programador puede decidir usar el identificador T para int o char. Es posible que un algoritmo de C ++ tenga más de un tipo genérico. Con, digamos, T para int o char, U puede representar el tipo float o pointer. Una clase, como la clase de cadena o vector, es como un tipo de datos, y los objetos instanciados son como valores del tipo de datos, que es la clase especificada. Entonces, el mecanismo de plantilla también permite al programador usar un identificador de tipo genérico para un conjunto de clases.

Una plantilla de C ++ crea un algoritmo independiente del tipo de datos empleados. Entonces, el mismo algoritmo, con muchas ocurrencias del mismo tipo, puede usar diferentes tipos en diferentes ejecuciones. Las entidades de variable, función, estructura y clase pueden tener plantillas. Este artículo explica cómo declarar plantillas, cómo definir plantillas y cómo aplicarlas en C++. Ya debe tener conocimiento de las entidades antes mencionadas para comprender los temas cubiertos en este artículo.

Tipos

Escalar

Los tipos escalares son void, bool, char, int, float y pointer.

Clases como tipos

Una clase particular se puede considerar como un tipo y sus objetos como posibles valores.

Un tipo genérico representa un conjunto de tipos escalares. La lista de tipos escalares es extensa. El tipo int, por ejemplo, tiene otros tipos relacionados, como short int, long int, etc. Un tipo genérico también puede representar un conjunto de clases.

Variable

Un ejemplo de una declaración y definición de plantilla es el siguiente:

plantilla
T pi = 3.14;

Antes de continuar, tenga en cuenta que este tipo de declaración no puede aparecer en la función main () ni en ningún alcance de bloque. La primera línea es la declaración de encabezado de plantilla, con el nombre de tipo genérico elegido por el programador, T. La siguiente línea es la definición del identificador, pi, que es del tipo genérico, T. La precisión, de si la T es un int o un flotante o algún otro tipo, se puede hacer en la función main () de C ++ (o alguna otra función). Tal precisión se hará con la variable pi, y no con T.

La primera línea es la declaración de encabezado de plantilla. Esta declaración comienza con la palabra reservada, la plantilla y luego los corchetes angulares abiertos y cerrados. Dentro de los corchetes angulares, hay al menos un identificador de tipo genérico, como T, arriba. Puede haber más de un identificador de tipo genérico, cada uno precedido por la palabra reservada, typename. Estos tipos genéricos en esa posición se denominan parámetros de plantilla.

La siguiente declaración se puede escribir en main () o en cualquier otra función:

cout << pi << '\n';

Y la función mostraría 3.14. La expresión pi decide el tipo exacto de T para la variable pi. La especialización decide el tipo de datos particular para el parámetro de plantilla. La instanciación es el proceso interno de C ++ para crear un tipo particular, como float, en este caso. No confunda entre crear una instancia de un parámetro de plantilla y crear una instancia de una clase. En el tema de la plantilla, muchos tipos de datos pueden tener un nombre de tipo genérico, mientras que muchas clases pueden tener un nombre de clase genérico. Sin embargo, el nombre de clase genérico para las clases simplemente se conoce como una clase, y no como un nombre de clase. Además, un valor es para un tipo de datos, como int, como un objeto instanciado es para una clase, como la clase String.

En la especialización, el tipo de datos elegido, como float, se coloca entre paréntesis angulares después de la variable. Si hay más de un parámetro de plantilla en la declaración de encabezado de plantilla, habrá un número correspondiente de tipos de datos en el mismo orden en la expresión de especialización.

En la especialización, un tipo se conoce como argumento de plantilla. No confunda entre esto y el argumento de la función para la llamada a la función.

Tipo predeterminado

Si no se proporciona ningún tipo en la especialización, se asume el tipo predeterminado. Entonces, de la siguiente expresión:

plantilla
U pi = "amor";
la pantalla de:
cout << pi<> << '\n';

es "amor" para el puntero constante a char. Tenga en cuenta en la declaración que U = const char *. Los corchetes angulares estarán vacíos en la especialización (no se proporciona ningún tipo); el tipo real se considera un puntero constante a char, el tipo predeterminado. Si se necesitara algún otro tipo en la especialización, entonces el nombre del tipo se escribiría entre corchetes angulares. Cuando se desea el tipo predeterminado en la especialización, repetir el tipo en los corchetes angulares es opcional, i.mi., los corchetes angulares se pueden dejar vacíos.

Nota: el tipo predeterminado aún se puede cambiar en la especialización al tener un tipo diferente.

estructura

El siguiente ejemplo muestra cómo se puede usar un parámetro de plantilla con una estructura:

plantilla Edades de estructura

T John = 11;
T Peter = 12;
T María = 13;
T Joy = 14;
;

Estas son las edades de los estudiantes en un grado (clase). La primera línea es la declaración de la plantilla. El cuerpo entre llaves es la definición real de la plantilla. Las edades se pueden generar en la función main () con lo siguiente:

Siglos grado 7;
cout << grade7.John << " << grade7.Mary << '\n';

La salida es: 11 13. La primera declaración aquí realiza la especialización. Note como se ha hecho. También le da un nombre a un objeto de la estructura: grade7. La segunda declaración tiene expresiones de objeto de estructura ordinarias. Una estructura es como una clase. Aquí, Ages es como un nombre de clase, mientras que grade7 es un objeto de la clase (estructura).

Si algunas edades son números enteros y otras son flotantes, entonces la estructura necesita dos parámetros genéricos, como sigue:

plantilla Edades de estructura

T John = 11;
U Peter = 12.3;
T María = 13;
U Alegría = 14.6;
;

Un código relevante para la función main () es el siguiente:

Siglos grado 7;
cout << grade7.John << " << grade7.Peter << '\n';

La salida es: 11 12.3. En la especialización, el orden de los tipos (argumentos) debe corresponder al orden de los tipos genéricos en la declaración.

La declaración de plantilla se puede separar de la definición de la siguiente manera:

plantilla Edades de estructura

T John;
U Peter;
T Mary;
U Joy;
;
Siglos grado7 = 11, 12.3, 13, 14.6;

El primer segmento de código es puramente una declaración de una plantilla (no hay asignaciones). El segundo segmento de código, que es solo una declaración, es la definición del identificador, grado7. El lado izquierdo es la declaración del identificador, grado 7. El lado derecho es la lista de inicializadores, que asigna los valores correspondientes a los miembros de la estructura. El segundo segmento (declaración) se puede escribir en la función main (), mientras que el primer segmento permanece fuera de la función main ().

No tipo

Ejemplos de tipos que no son de datos incluyen int, pointer to object, pointer to function y auto types. Hay otros no tipos, que este artículo no aborda. Un no tipo es como un tipo incompleto, cuyo valor se da más adelante y no se puede cambiar. Como parámetro, comienza con un no tipo particular, seguido de un identificador. El valor del identificador se da más tarde, en la especialización, y no se puede volver a cambiar (como una constante, cuyo valor se da más adelante). El siguiente programa ilustra esto:

#incluir
usando el espacio de nombres std;
plantilla Edades de estructura

T John = N;
U Peter = 12.3;
T María = N;
U Alegría = 14.6;
;
int main ()

Siglos grado 7;
cout << grade7.John << " << grade7.Joy << '\n';
return 0;

En la especialización, el primer tipo, int, en los corchetes angulares es más por formalidad, para asegurarse de que el número y el orden de los parámetros se correspondan con el número y el orden de los tipos (argumentos). El valor de N se ha dado en la especialización. La salida es: 11 14.6.

Especialización parcial

Supongamos que una plantilla tiene cuatro tipos genéricos y que, entre los cuatro tipos, se necesitan dos tipos predeterminados. Esto se puede lograr utilizando la construcción de especialización parcial, que no emplea el operador de asignación. Entonces, la construcción de especialización parcial da valores predeterminados a un subconjunto de tipos genéricos. Sin embargo, en el esquema de especialización parcial, se necesitan una clase base (estructura) y una clase de especialización parcial (estructura). El siguiente programa ilustra esto para un tipo genérico de entre dos tipos genéricos:

#incluir
usando el espacio de nombres std;
// clase de plantilla base
plantilla
Edades de estructura

;
// especialización parcial
plantilla
Edades de estructura

T1 John = 11;
Peter flotante = 12.3;
T1 María = 13;
flotar Alegría = 14.6;
;
int main ()

Siglos grado 7;
cout << grade7.John << " << grade7.Joy << '\n';
return 0;

Identificar la declaración de clase base y su definición de clase parcial. La declaración de encabezado de plantilla de la clase base tiene todos los parámetros genéricos necesarios. La declaración de encabezado de plantilla de la clase de especialización parcial solo tiene el tipo genérico. Hay un conjunto adicional de corchetes angulares usados ​​en el esquema que viene justo después del nombre de la clase en la definición de especialización parcial. Es lo que realmente hace la especialización parcial. Tiene el tipo predeterminado y el tipo no predeterminado, en el orden escrito en la clase base. Tenga en cuenta que el tipo predeterminado aún puede recibir un tipo diferente en la función main ().

El código relevante en la función main () puede ser el siguiente:

Siglos grado 7;
cout << grade7.John << " << grade7.Joy << '\n';

La salida es: 11 14.6.

Paquete de parámetros de plantilla

Un paquete de parámetros es un parámetro de plantilla que acepta cero o más tipos genéricos de plantilla para los tipos de datos correspondientes. El parámetro del paquete de parámetros comienza con la palabra reservada typename o class. A esto le siguen tres puntos y luego el identificador del paquete. El siguiente programa ilustra cómo se puede usar un paquete de parámetros de plantilla con una estructura:

#incluir
usando el espacio de nombres std;
plantilla Edades de estructura

int John = 11;
Peter flotante = 12.3;
int Mary = 13;
flotar Alegría = 14.6;
;
int main ()

Siglos grado B;
cout << gradeB.John << " << gradeB.Mary << '\n';
Siglos gradeC;
cout << gradeC.Peter << " << gradeC.Joy << '\n';
Siglos gradeD;
cout << gradeD.John << " << gradeD.Joy << '\n';
Edades <> gradoA; // como por defecto
cout << gradeA.John << " << gradeA.Joy << '\n';
return 0;

La salida es:

11 13
12.3 14.6
11 14.6
11 14.6

Plantillas de funciones

Las características de la plantilla mencionadas anteriormente se aplican de manera similar a las plantillas de funciones. El siguiente programa muestra una función con dos parámetros de plantilla genéricos y tres argumentos:

#incluir
usando el espacio de nombres std;
plantilla void func (T no, U cha, const char * str)

cout << "There are " << no << " books worth " << cha << str << " in the store." << '\n';

int main ()

func (12, '$', "500");
return 0;

El resultado es el siguiente:

Hay 12 libros por valor de $ 500 en la tienda.

Separación del prototipo

La definición de la función se puede separar de su prototipo, como muestra el siguiente programa:

#incluir
usando el espacio de nombres std;
plantilla void func (T no, U cha, const char * str);
plantilla void func (T no, U cha, const char * str)

cout << "There are " << no << " books worth " << cha << str << " in the store." << '\n';

int main ()

func (12, '$', "500");
return 0;

Nota: La declaración de la plantilla de función no puede aparecer en la función main () ni en ninguna otra función.

Sobrecarga

La sobrecarga de la misma función puede tener lugar con diferentes declaraciones de encabezado de plantilla. El siguiente programa ilustra esto:

#incluir
usando el espacio de nombres std;
plantilla void func (T no, U cha, const char * str)

cout << "There are " << no << " books worth " << cha << str << " in the store." << '\n';

plantilla void func (T no, const char * str)

cout << "There are " << no << " books worth $" << str << " in the store." << '\n';

int main ()

func (12, '$', "500");
func (12, "500");
return 0;

La salida es:

Hay 12 libros por valor de $ 500 en la tienda.

Hay 12 libros por valor de $ 500 en la tienda.

Plantillas de clase

Las características de las plantillas mencionadas anteriormente se aplican de manera similar a las plantillas de clase. El siguiente programa es la declaración, definición y uso de una clase simple:

#incluir
usando el espacio de nombres std;
clase TheCla

público:
int num;
static char ch;
void func (char cha, const char * str)

cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';

diversión del vacío estático (char ch)

si (ch == 'a')
cout << "Official static member function" << '\n';

;
int main ()

TheCla obj;
obj.num = 12;
obj.func ('$', "500");
return 0;

El resultado es el siguiente:

Hay 12 libros por valor de $ 500 en la tienda.

El siguiente programa es el programa anterior con una declaración de encabezado de plantilla:

#incluir
usando el espacio de nombres std;
plantilla clase TheCla

público:
T num;
estático U ch;
void func (U cha, const char * str)

cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';

diversión del vacío estático (U ch)

si (ch == 'a')
cout << "Official static member function" << '\n';

;
int main ()

TheCla obj;
obj.num = 12;
obj.func ('$', "500");
return 0;

En lugar de la palabra typename en la lista de parámetros de la plantilla, se puede usar la palabra class. Tenga en cuenta la especialización en la declaración del objeto. La salida sigue siendo la misma:

Hay 12 libros por valor de $ 500 en la tienda.

Declaración de separación

La declaración de plantilla de clase se puede separar del código de clase, de la siguiente manera:

plantilla clase TheCla;
plantilla clase TheCla

público:
T num;
estático U ch;
void func (U cha, const char * str)

cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';

diversión del vacío estático (U ch)

si (ch == 'a')
cout << "Official static member function" << '\n';

;

Tratar con miembros estáticos

El siguiente programa muestra cómo acceder a un miembro de datos estáticos y una función de miembro estático:

#incluir
usando el espacio de nombres std;
plantilla clase TheCla

público:
T num;
estático U ch;
void func (U cha, const char * str)

cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';

diversión del vacío estático (U cha)

si (ch == 'a')
cout << "Official static member function" << cha << '\n';

;
plantilla U TheCla:: ch = 'a';
int main ()

TheCla::divertida('.');
return 0;

Asignar un valor a un miembro de datos estáticos es una declaración y no puede estar en main (). Tenga en cuenta el uso y las posiciones de los tipos genéricos y el tipo genérico de datos en la declaración de asignación. Además, tenga en cuenta que la función de miembro de datos estáticos se ha llamado en main (), con los tipos de datos de plantilla reales. El resultado es el siguiente:

Función de miembro estático oficial.

Compilando

La declaración (encabezado) y la definición de una plantilla deben estar en un archivo. Es decir, deben estar en la misma unidad de traducción.

Conclusión

Las plantillas de C ++ hacen que un algoritmo sea independiente del tipo de datos empleados. Las entidades de variable, función, estructura y clase pueden tener plantillas, que implican declaración y definición. La creación de una plantilla también implica especialización, que es cuando un tipo genérico toma un tipo real. La declaración y la definición de una plantilla deben estar en una unidad de traducción.

Cómo descargar y jugar Sid Meier's Civilization VI en Linux
Introducción al juego Civilization 6 es una versión moderna del concepto clásico introducido en la serie de juegos Age of Empires. La idea era bastant...
Cómo instalar y jugar a Doom en Linux
Introducción a Doom La serie Doom se originó en los años 90 después del lanzamiento del Doom original. Fue un éxito instantáneo y, desde ese momento e...
Vulkan para usuarios de Linux
Con cada nueva generación de tarjetas gráficas, vemos que los desarrolladores de juegos superan los límites de la fidelidad gráfica y se acercan un pa...