Las plantillas son la herramienta de C++ para admitir polimorfismo parametrizado. El uso de plantillas permite a los usuarios declarar un patrón general para clases o funciones, de modo que ciertos miembros de datos de la clase o los parámetros y valores de retorno de las funciones miembro puedan tomar cualquier tipo.

Una plantilla es una herramienta que parametriza los tipos;

Generalmente hay dos formas: plantillas de función y plantillas de clase;

Las plantillas de función se aplican a funciones que difieren solo en los tipos de los parámetros;

Las plantillas de clase se aplican a clases que difieren solo en los tipos de los miembros de datos y de las funciones miembro.

El propósito de usar plantillas es permitir a los programadores escribir código independiente del tipo. Por ejemplo, si se escribe una función swap para intercambiar dos enteros int, esta función solo puede manejar el tipo int; no puede manejar tipos como double o char. Para intercambiar esos tipos sería necesario escribir otra función swap. El propósito de usar plantillas es hacer que la implementación del programa sea independiente del tipo; por ejemplo, una plantilla de función swap puede realizar intercambios tanto de tipo int como de tipo double. Las plantillas se pueden aplicar a funciones y clases. A continuación se presentan por separado.

Nota:La declaración o definición de una plantilla solo puede realizarse en el ámbito global, en un espacio de nombres o dentro de una clase. Es decir, no puede realizarse en un ámbito local ni dentro de una función; por ejemplo, no se puede declarar o definir una plantilla en la función main.


I. Forma general de la plantilla de función

1. Formato de la plantilla de función:

template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表)
{
    函数体
}

Donde template y class son palabras clave; class puede reemplazarse por la palabra clave typename. Aquí typename y class no tienen diferencia. Los parámetros entre paréntesis angulares <> se llaman parámetros formales de plantilla. Los parámetros de plantilla son muy similares a los parámetros de función; los parámetros de plantilla no pueden estar vacíos. Una vez declarada una plantilla de función, se pueden usar los nombres de los parámetros de la plantilla de función para declarar miembros de datos y funciones miembro; es decir, en cualquier lugar de la función donde se puedan usar tipos integrados, se puede usar el nombre del parámetro de plantilla. Los parámetros de plantilla deben inicializarse con los argumentos de plantilla proporcionados al llamar a la plantilla de función. Una vez que el compilador determina el tipo real del argumento de plantilla, se dice que se ha instanciado una instancia de la plantilla de función. Por ejemplo, la forma de la plantilla de función swap es:

template <class T> void swap(T& a, T& b){},

Cuando se llama a dicha plantilla de función, el tipo T será reemplazado por el tipo en el momento de la llamada, por ejemploswap(a,b)dondeaybSíinttipo, en este momento los parámetros formales en la plantilla de función swapTseránintreemplazados, y la plantilla de función se convierte enswap(int &a, int &b). Y cuandoswap(c,d)dondecydSídoubletipo, la plantilla de función será reemplazada porswap(double &a, double &b), logrando así que la implementación de la función sea código independiente del tipo.

2. Nota: para las plantillas de función, no existeh(int,int)una llamada como esta,no se puede especificar el tipo de los parámetros de plantilla en los argumentos de la llamada a la función; para llamar a una plantilla de función se debe usarla deducción de argumentospara llevarla a cabo, es decir, solo se puede realizar h(2,3)una llamada de este tipo, oint a, b; h(a,b)。

¡Los ejemplos de demostración de plantillas de función se tratarán más adelante!


II. Forma general de la plantilla de clase

1. El formato de la plantilla de clase es:

template<class  形参名,class 形参名,…>   class 类名
{ ... };

Las plantillas de clase y las plantillas de función comienzan con template seguido de una lista de parámetros de plantilla. Los parámetros de plantilla no pueden estar vacíos. Una vez declarada una plantilla de clase, se pueden usar los nombres de los parámetros de plantilla de la clase para declarar miembros de datos y funciones miembro; es decir, en cualquier lugar de la clase donde se puedan usar tipos integrados, se puede usar el nombre del parámetro de plantilla para declarar. Por ejemplo

template<class T> class A{public: T a; T b; T hy(T c, T &d);};

En la clase A se declaran dos miembros de datos a y b de tipo T, y también se declara una función hy cuyo tipo de retorno es T y cuyos dos parámetros son de tipo T.

2. Creación de objetos de plantillas de clase: por ejemplo, una clase de plantillaA, entonces el método para crear un objeto usando la plantilla de clase esA<int> m;Después de la claseAseguirle con un<>corchete angular y rellenarlo con el tipo correspondiente; de esta manera, en la claseAtodos los lugares donde se usen los parámetros de plantilla seránintreemplazados por él. Cuando la plantilla de clase tiene dos parámetros de plantilla, el método de creación del objeto esA<int, double> m;Los tipos se separan con comas.

3. Para las plantillas de clase, el tipo de los parámetros de plantilla debe especificarse explícitamente entre corchetes angulares después del nombre de la clase. Por ejemploA<2> m;Usar este método para establecer el parámetro de plantilla comointes incorrecto (Error de compilación: error C2079: 'a' uses undefined class 'A<int>'),Para los parámetros de plantilla de clase no existe el problema de deducción de argumentos.Es decir, no se puede tomar un valor entero2y deducirlo comointtipo para pasarlo al parámetro de plantilla. Para establecer el parámetro de plantilla de clase comointtipo, se debe especificar de esta maneraA<int> m。

4. El método para definir funciones miembro fuera de la plantilla de clase es:

template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体},

Por ejemplo, una clase A con dos parámetros de plantilla T1, T2 contiene una función void h(); la sintaxis para definir dicha función es:

template<class T1,class T2> void A<T1,T2>::h(){}。

Nota:Cuando se definen miembros de la clase fuera de la clase, los parámetros de plantilla después de template deben coincidir con los parámetros de plantilla de la clase que se va a definir.

5. Recordatorio otra vez: la declaración o definición de una plantilla solo puede realizarse en el ámbito global, en un espacio de nombres o dentro de una clase. Es decir, no puede realizarse en un ámbito local ni dentro de una función; por ejemplo, no se puedemaindeclarar o definir una plantilla en la función.


III. Parámetros de plantilla

Hay tres tipos de parámetros de plantilla: parámetros de tipo, parámetros no tipo y parámetros de plantilla.

1. Parámetro de tipo

1.1 Parámetro de plantilla de tipo:Un parámetro de tipo se compone de la palabra clave class o typename seguida de un especificador, comotemplate<class T> void h(T a){}; dondeTes un parámetro de tipo; el nombre del parámetro de tipo lo determina el propio usuario.Un parámetro de plantilla representa un tipo desconocido.Los parámetros de tipo de plantilla pueden usarse como especificadores de tipo en cualquier lugar dentro de la plantilla, exactamente igual que los especificadores de tipos integrados o especificadores de tipos de clase; es decir, pueden usarse para especificar tipos de retorno, declaraciones de variables, etc.

Versión original del autor:1.2, No se pueden especificar dos tipos diferentes para el mismo parámetro de tipo de plantilla, por ejemplotemplate<class T>void h(T a, T b){},la llamadah(2, 3.2)producirá un error, porque la declaración asigna al mismo parámetro de plantillaTdos tipos diferentes; el primer argumento2establece el parámetro de plantilla T comoint, mientras que el segundo argumento3.2establece el parámetro de plantilla comodouble; los dos tipos de parámetros son inconsistentes y causará un error.(Esto es para plantillas de función)

Versión original del autor: 1.2 es correcto para plantillas de función, pero ignora las plantillas de clase. A continuación se complementará el caso de las plantillas de clase.

Suplemento de 1.2 añadido por mí (para plantillas de clase):Cuando declaramos un objeto de clase como:A<int> a, por ejemplotemplate<class T>T g(T a, T b){}, la llamadaa.g(2, 3.2)no producirá un error en tiempo de compilación, pero habrá una advertencia, porque al declarar el objeto de la clase ya se haTconvertidointal tipo, mientras que el segundo argumento3.2establece el parámetro de plantilla comodouble; en tiempo de ejecución, se realizará una conversión forzada de3.2a3. Cuando declaramos el objeto de la clase como:A<double> a, en este caso no habrá la advertencia mencionada, porque desdeintllegardoublees una conversión automática de tipo.

Ejemplo de demostración 1:

TemplateDemo.h

#ifndef TEMPLATE_DEMO_HXX #define TEMPLATE_DEMO_HXX template<class T> class A{ public: T g(T a,T b); A(); }; #endif   TemplateDemo.cpp #include<iostream.h> #include "TemplateDemo.h" template<class T> A<T>::A(){} template<class T> T A<T>::g(T a,T b){ return a+b; } void main(){ A<int> a; cout<<a.g(2,3.2)<<endl; }

Resultado de compilación:

--------------------Configuration: TemplateDemo - Win32 Debug--------------------
Compiling...
TemplateDemo.cpp
G:\C++\CDaima\TemplateDemo\TemplateDemo.cpp(12) : warning C4244: 'argument' : conversion from 'const double' to 'int', possible loss of data

TemplateDemo.obj - 0 error(s), 1 warning(s)

Resultado de ejecución:5 

 

De los ejemplos de prueba anteriores podemos ver que no es tan riguroso como en el original del autor. ¡Esto es solo el resultado de mis propias pruebas! Por favor, con una actitud de buscar la verdad, verifíquenlo ustedes mismos.

2. Parámetros no tipo

2.1 Parámetro de plantilla no tipo:Los parámetros no tipo de una plantilla son parámetros de tipos integrados, comotemplate<class T, int a> class B{};dondeint aes un parámetro de plantilla no tipo.

2.2 Los parámetros no tipo son valores constantes dentro de la definición de la plantilla; es decir, los parámetros no tipo son constantes dentro de la plantilla.

    2.3, Los parámetrosde plantillano tipo solo pueden ser de tipo entero, puntero y referencia; tipos como double, String, String** no están permitidos.Perodouble &,double *,la referencia o puntero a un objeto es correcto.

    2.4, El argumento pasado a un parámetro de plantilla no tipo debe ser una expresión constante, es decir, debe poder evaluarse en tiempo de compilación.

2.5 Nota:Cualquier objeto local, variable local, dirección de objeto local, dirección de variable local no son expresiones constantes y no pueden usarse como argumentos para parámetros de plantilla no tipo. Los tipos de puntero globales, variables globales y objetos globales tampoco son expresiones constantes y no pueden usarse como argumentos para parámetros de plantilla no tipo.

    2.6, ; A<b> m; es decir, conversión de array a puntero.。

    2.7 、sizeofEl resultado de una expresión es una expresión constante y también puede usarse como argumento real de un parámetro de plantilla no tipo.

2.8. Cuando el parámetro de la plantilla es de tipo entero, el argumento real al invocar la plantilla debe ser de tipo entero y constante durante la compilación, por ejemplo:template <class T, int a> class A{};Si tenemosint b, entonces A<int, b> m;se producirá un error, porquebno es constante; siconst int b, entonces A<int, b> m; es correcto, porque en este casobes constante.

    2.9 、Los parámetros no tipo generalmente no se aplican en plantillas de función., por ejemplo, si tenemos la plantilla de funcióntemplate<class T, int a> void h(T b){}, si se utilizah(2)la llamada producirá un error de que no se puede deducir el argumento para el parámetro no tipo a. Para este tipo de plantilla de función se puede resolver con argumentos de plantilla explícitos, como usar h<int, 3>(2), de modo que el parámetro no tipo a se establece como el entero 3. Los argumentos de plantilla explícitos se presentarán más adelante.

2.10. Conversiones permitidas entre los parámetros formales y los argumentos reales de los parámetros de plantilla no tipo.
1. Se permiten conversiones de array a puntero y de función a puntero. Por ejemplo: template <int *a> class A{}; int b
2. Conversión del modificador const. Por ejemplo: template<const int *a> class A{}; int b; A<&b> m; es decir, la conversión de int * a const int *.
3. Conversión de promoción. Por ejemplo: template<int a> class A{}; const short b=2; A<b> m; es decir, la conversión de promoción de short a int.
4. Conversión de valores enteros. Por ejemplo: template<unsigned int a> class A{}; A<3> m; es decir, la conversión de int a unsigned int.
5. Conversión habitual.

Ejemplo de demostración 1 de parámetros no tipo:

  El usuario especifica personalmente el tamaño de la pila e implementa las operaciones relacionadas con la pila.

TemplateDemo.h

#ifndef TEMPLATE_DEMO_HXX #define TEMPLATE_DEMO_HXX template<class T,int MAXSIZE> class Stack{//MAXSIZE lo establece el usuario al crear el objeto private: T elems[MAXSIZE]; // Array que contiene los elementos int numElems; // Número total actual de elementos public: Stack(); // Constructor void push(T const&); // Empujar elemento void pop(); // Extraer elemento T top() const; // Devuelve el elemento de la cima de la pila bool empty() const{ // Devuelve si la pila está vacía return numElems == 0; } bool full() const{ // Devuelve si la pila está llena return numElems == MAXSIZE; } }; template <class T,int MAXSIZE> Stack<T,MAXSIZE>::Stack():numElems(0){ // Inicialmente la pila no contiene elementos // No hace nada } template <class T,int MAXSIZE> void Stack<T, MAXSIZE>::push(T const& elem){ if(numElems == MAXSIZE){ throw std::out_of_range("Stack<>::push(): stack is full"); } elems[numElems] = elem; // Añade un elemento ++numElems; // Aumenta el número de elementos } template<class T,int MAXSIZE> void Stack<T,MAXSIZE>::pop(){ if (numElems <= 0) { throw std::out_of_range("Stack<>::pop(): empty stack"); } --numElems; // Disminuye el número de elementos } template <class T,int MAXSIZE> T Stack<T,MAXSIZE>::top()const{ if (numElems <= 0) { throw std::out_of_range("Stack<>::top(): empty stack"); } return elems[numElems-1]; // Devuelve el último elemento } #endif

TemplateDemo.cpp

#include<iostream.h> #include <iostream> #include <string> #include <cstdlib> #include "TemplateDemo.h" int main(){ try { Stack<int,20> int20Stack; // Pila que puede almacenar 20 elementos int Stack<int,40> int40Stack; // Pila que puede almacenar 40 elementos int Stack<std::string,40> stringStack; // Pila que puede almacenar 40 elementos string // Usa una pila que puede almacenar 20 elementos int int20Stack.push(7); std::cout << int20Stack.top() << std::endl; //7 int20Stack.pop(); // Usa una pila que puede almacenar 40 string stringStack.push("hello"); std::cout << stringStack.top() << std::endl; //hello stringStack.pop(); stringStack.pop(); //Exception: Stack<>::pop<>: empty stack return 0; } catch (std::exception const& ex) { std::cerr << "Exception: " << ex.what() << std::endl; return EXIT_FAILURE; // Sale del programa con la marca ERROR } }

Ejemplo de demostración 2 de parámetros no tipo:

TemplateDemo01.h

#ifndef TEMPLATE_DEMO_O1 #define TEMPLATE_DEMO_01 template<typename T> class CompareDemo{ public: int compare(const T&, const T&); }; template<typename T> int CompareDemo<T>::compare(const T& a,const T& b){ if((a-b)>0) return 1; else if((a-b)<0) return -1; else return 0; } #endif

TemplateDemo01.cpp

#include<iostream.h> #include "TemplateDemo01.h" void main(){ CompareDemo<int> cd; cout<<cd.compare(2,3)<<endl; }

Resultado de la ejecución:-1 

TemplateDemo01.cpp

#include<iostream.h> #include "TemplateDemo01.h" void main(){ CompareDemo<double> cd; cout<<cd.compare(3.2,3.1)<<endl; }

Resultado de la ejecución:1 

 

TemplateDemo01.h

#ifndef TEMPLATE_DEMO_O1 #define TEMPLATE_DEMO_01 template<typename T> class CompareDemo{ public: int compare(T&, T&); }; template<typename T> int CompareDemo<T>::compare(T& a,T& b){ if((a-b)>0) return 1; else if((a-b)<0) return -1; else return 0; } #endif

TempalteDemo01.cpp

#include<iostream.h> #include "TemplateDemo01.h" void main(){ CompareDemo<int> cd; int a=2,b=3; cout<<cd.compare(a,b)<<endl; }

Ejemplo de demostración 3 de parámetros no tipo:

TemplateDemo02.cpp

#include<iostream.h> template<typename T> const T& max(const T& a,const T& b){ return a>b ? a:b; } void main(){ cout<<max(2.1,2.2)<<endl;// El argumento de plantilla se deduce implícitamente como double cout<<max<double>(2.1,2.2)<<endl;// Especifica explícitamente los parámetros de plantilla. cout<<max<int>(2.1,2.2)<<endl;// Los parámetros de plantilla especificados explícitamente convertirán directamente la función a int. }

Resultado de la ejecución:

cout<<max<int>(2.1,2.2)<<endl; //显示指定的模板参数,会将函数函数直接转换为int.

Esta declaración producirá una advertencia:

--------------------Configuration: TemplateDemo02 - Win32 Debug--------------------
Compiling...
TemplateDemo02.cpp
G:\C++\CDaima\TemplateDemo02\TemplateDemo02.cpp(11) : 
  warning C4244: 'argument' : conversion from 'const double' to 'const int', possible loss of data
G:\C++\CDaima\TemplateDemo02\TemplateDemo02.cpp(11) : 
  warning C4244: 'argument' : conversion from 'const double' to 'const int', possible loss of data

TemplateDemo02.obj - 0 error(s), 2 warning(s)

IV. Parámetros de tipo por defecto en plantillas de clase

1, Se pueden proporcionar valores predeterminados para los parámetros de tipo de las plantillas de clase, pero no se pueden proporcionar valores predeterminados para los parámetros de tipo de las plantillas de función.Tanto las plantillas de función como las plantillas de clase pueden proporcionar valores predeterminados para los parámetros no tipo de la plantilla.

2. La forma de los valores predeterminados de los parámetros de tipo de una plantilla de clase es:template<class T1, class T2=int> class A{};para el segundo parámetro de tipo de plantillaT2proporcionarintun valor predeterminado de tipo.

3. Los valores predeterminados de los parámetros de tipo de una plantilla de clase son como los parámetros predeterminados de una función: si hay varios parámetros de tipo, todos los parámetros de plantilla posteriores al primer parámetro que ha establecido un valor predeterminado también deben establecer un valor predeterminado, por ejemplotemplate<class T1=int, class T2>class A{};es incorrecto, porqueT1se dio un valor predeterminado, peroT2no se estableció.

4. Al definir miembros de la clase fuera de la plantilla de clase,templatela lista de parámetros después de template debe omitir los tipos de parámetros predeterminados. Por ejemplotemplate<class  T1, class T2=int> class A{public: void h();}; el método de definición estemplate<class T1,class T2> void A<T1,T2>::h(){}。

Definir los parámetros de tipo de la plantilla de clase:

Ejemplo de demostración 1:

TemplateDemo.h

#ifndef TEMPLATE_DEMO_HXX #define TEMPLATE_DEMO_HXX template<class T> class A{ public: T g(T a,T b); A(); }; #endif

TemplateDemo.cpp

#include<iostream.h> #include "TemplateDemo.h" template<class T> A<T>::A(){} template<class T> T A<T>::g(T a,T b){ return a+b; } void main(){ A<int> a; cout<<a.g(2,3)<<endl; }

Resultado de la ejecución:5

Ejemplo 1 de parámetros de tipo por defecto en plantillas de clase:

TemplateDemo03.h

#ifndef TEMPLATE_DEMO_03 #define TEMPLATE_DEMO_03 // Define una plantilla de clase con parámetros de tipo predeterminados. Aquí T2 se establece por defecto como tipo int. template<class T1,class T2=int> class CeilDemo{ public: int ceil(T1,T2); }; // Al definir miembros de la clase fuera de la plantilla de clase, la lista de parámetros después de template debe omitir los tipos de parámetros predeterminados. template<class T1,class T2> int CeilDemo<T1,T2>::ceil(T1 a,T2 b){ return a>>b; } #endif

TemplateDemo03.cpp

#include<iostream.h> #include "TemplateDemo03.h" void main(){ CeilDemo<int> cd; cout<<cd.ceil(8,2)<<endl; }

Resultado de la ejecución:2 

Al definir miembros de la clase fuera de la plantilla de clase, la lista de parámetros después de template debe omitir los tipos de parámetros predeterminados. Si no se omiten, no se producirá un error de compilación, sino una advertencia:

--------------------Configuration: TemplateDemo03 - Win32 Debug--------------------
Compiling...
TemplateDemo03.cpp
g:\c++\cdaima\templatedemo03\templatedemo03.h(12) : 
warning C4519: default template arguments are only allowed on a class template; ignored

TemplateDemo03.obj - 0 error(s), 1 warning(s)

Autor original:Los valores predeterminados de los parámetros de tipo de una plantilla de clase son iguales a los parámetros predeterminados de una función: si hay varios parámetros de tipo, todos los parámetros de plantilla posteriores al primer parámetro que ha establecido un valor predeterminado deben establecer un valor predeterminado, por ejemplotemplate<class T1=int, class T2>class A{};es incorrecto, porqueT1se dio un valor predeterminado, peroT2no se estableció.

Ejemplo 2 de parámetros de tipo por defecto en plantillas de clase:

TemplateDemo03.h

#ifndef TEMPLATE_DEMO_03 #define TEMPLATE_DEMO_03 template<class T1=int,class T2,class T3> class CeilDemo{ public: int ceil(T1,T2,T3); }; template<class T1,class T2,class T3> int CeilDemo<T1,T2,T3>::ceil(T1 a,T2 b,T3 c){ return a+b+c; } #endif

TemplateDemo03.cpp

#include<iostream.h> #include "TemplateDemo03.h" void main(){ CeilDemo<int,int> cd; cout<<cd.ceil(2,3,4)<<endl; }

Resultado de la ejecución:9  

TemplateDemo03.h

#ifndef TEMPLATE_DEMO_03 #define TEMPLATE_DEMO_03 template<class T1=int,class T2,class T3> class CeilDemo{ public: double ceil(T1,T2,T3); }; template<class T1,class T2,class T3> double CeilDemo<T1,T2,T3>::ceil(T1 a,T2 b,T3 c){ return a+b+c; } #endif

TemplateDemo03.cpp

#include<iostream.h> #include "TemplateDemo03.h" void main(){ CeilDemo<double,double> cd; cout<<cd.ceil(2,3.1,4.1)<<endl; }

Error de compilación:

--------------------Configuration: TemplateDemo03 - Win32 Debug--------------------
Compiling...
TemplateDemo03.cpp
g:\c++\cdaima\templatedemo03\templatedemo03.h(12) : 
error C2244: 'CeilDemo<T1,T2,T3>::ceil' : unable to resolve function overload
g:\c++\cdaima\templatedemo03\templatedemo03.cpp(6) : 
error C2065: 'cd' : undeclared identifier
g:\c++\cdaima\templatedemo03\templatedemo03.cpp(6) : 
error C2228: left of '.ceil' must have class/struct/union type
Error executing cl.exe.

TemplateDemo03.obj - 3 error(s), 0 warning(s)

Del ejemplo anterior podemos ver que cuando intentamos definir T2 y T3 como tipo double, aparecerá un error (T1 está definido por defecto como tipo int). Entonces, si siguiéramos lo que dice el autor y estableciéramos T2 y T3 también con el valor predeterminado double, ¿seguiría apareciendo el error? Veamos el siguiente ejemplo:

Ejemplo 4 de parámetros de tipo por defecto en plantillas de clase:

TemplateDemo03.h

#ifndef TEMPLATE_DEMO_03 #define TEMPLATE_DEMO_03 template<class T1=int,class T2=double,class T3=double> class CeilDemo{ public: double ceil(T1,T2,T3); }; template<class T1,class T2,class T3> double CeilDemo<T1,T2,T3>::ceil(T1 a,T2 b,T3 c){ return a+b+c; } #endif

TemplateDemo03.cpp

#include<iostream.h> #include "TemplateDemo03.h" void main(){ CeilDemo<int,double,double> cd; cout<<cd.ceil(2,3.1,4.1)<<endl; }

Error de compilación:

--------------------Configuration: TemplateDemo03 - Win32 Debug--------------------
Compiling...
TemplateDemo03.cpp
g:\c++\cdaima\templatedemo03\templatedemo03.h(12) : 
error C2244: 'CeilDemo<T1,T2,T3>::ceil' : unable to resolve function overload
g:\c++\cdaima\templatedemo03\templatedemo03.cpp(6) : 
error C2065: 'cd' : undeclared identifier
g:\c++\cdaima\templatedemo03\templatedemo03.cpp(6) : 
error C2228: left of '.ceil' must have class/struct/union type
Error executing cl.exe.

TemplateDemo03.obj - 3 error(s), 0 warning(s)

Del resultado podemos ver que es el mismo error que en el ejemplo anterior. De los ejemplos podemos resumir lo siguiente: si una plantilla de clase tiene varios parámetros de tipo, al usar valores predeterminados para los parámetros de tipo, estos deben colocarse lo más al final posible de la lista de parámetros, y los tipos de parámetros predeterminados deben ser los mismos. Si desde el primer parámetro se establece un valor predeterminado, todos los parámetros de plantilla posteriores deben establecer un valor predeterminado del mismo tipo que el primer parámetro.(Declaración: también soy nuevo en C++, lo anterior son solo algunas dudas que planteo al autor original después de las demostraciones con ejemplos. Puede que mis ejemplos tengan deficiencias. Espero que los expertos no escatimen en enseñarme, para mejorar juntos este blog y proporcionar una plataforma de aprendizaje para los novatos como yo).

A continuación, verifiquemos que "no se pueden proporcionar valores predeterminados para los parámetros de tipo de las plantillas de función":

Ejemplo 5 de parámetros de tipo por defecto en plantillas de clase:

TemplateDemo04.cpp

#include<iostream.h> template<class T1,class T2,class T3> T1 sum(T1 a,T2 b,T3 c=int){ return a+b+c; } void main(){ cout<<sum<double,double>(1.1,2.1,3)<<endl; }

Error de compilación:

--------------------Configuration: TemplateDemo04 - Win32 Debug--------------------
Compiling...
TemplateDemo04.cpp
g:\c++\cdaima\templatedemo04\templatedemo04.cpp(4) : 
error C2062: type 'int' unexpected
Error executing cl.exe.

TemplateDemo04.obj - 1 error(s), 0 warning(s)

TemplateDemo.cpp después del cambio

TemplateDemo.cpp

#include<iostream.h> template<class T1,class T2,class T3> T1 sum(T1 a,T2 b,T3 c){ return a+b+c; } void main(){ cout<<sum<double,short,int>(1.1,3,257)<<endl; }

Resultado de la ejecución:261.1 

    

El ejemplo de demostración del autor original es el siguiente:

Ejemplo de parámetros no tipo en plantillas de clase

// La declaración o definición de una plantilla solo puede realizarse en el ámbito global, de espacio de nombres o de clase. Es decir, no puede realizarse en un ámbito local ni dentro de una función; por ejemplo, no se puede declarar o definir una plantilla en la función main. // Definición de la plantilla de clase template<class T>class A{public:T g(T a, T b); A();}; // Define la clase A con un parámetro de tipo de plantilla T template<class T1,class T2>class B{public:void g();}; // Define la clase B con dos parámetros de tipo de plantilla T1, T2 // Define los parámetros de tipo predeterminados de la plantilla de clase; los parámetros de tipo predeterminados no son aplicables a plantillas de función. template<class T1,class T2=int> class D{public: voidg();}; // Define una plantilla de clase con parámetros de tipo predeterminados. Aquí T2 se establece por defecto como tipo int. //template<class T1=int, class T2>class E{}; // Error: si se establece un parámetro de tipo predeterminado para T1, todos los parámetros posteriores a T1 deben tener valores predeterminados. // Las siguientes son definiciones de parámetros no tipo // Los parámetros no tipo solo pueden ser enteros, punteros y referencias. Tipos como double, String, String ** no están permitidos. Pero double &, double * son referencias o punteros a objetos,son correctos.template<class T1,int a> class Ci{public:void g();}; // Define el parámetro no tipo de la plantilla; el parámetro es de tipo entero template<class T1,int &a>class Cip{public:void g();}; template<class T1,A<int>* m> class Cc{public:void g();}; // Define el parámetro no tipo de la plantilla; el parámetro es un puntero a un objeto de la clase A de tipo int. template<class T1,double*a>class Cd{public:void g();}; // Define el parámetro no tipo de la plantilla; el parámetro es una referencia de tipo double. class E{}; template<class T1,E &m> class Ce{}; // El parámetro de plantilla no tipo es una referencia a un objeto. // Las siguientes declaraciones de parámetros no tipo son incorrectas. //template<class T1,A m>class Cc{}; // Error: un objeto no puede ser un parámetro no tipo; el tipo de un parámetro de plantilla no tipo solo puede ser referencia o puntero a un objeto. //template<class T1,double a>class Cc{}; // Error: el parámetro de una plantilla no tipo no puede ser de tipo double; puede ser una referencia a double. //template<class T1,A<int> m>class Cc{}; // Error: el parámetro de una plantilla no tipo no puede ser un objeto, debe ser una referencia o puntero a un objeto. Esta regla para los parámetros de plantillatampoco es una excepción.// Métodos para definir varios miembros de la clase fuera de la plantilla de clase, //typeid(nombre de variable).name() se utiliza para extraer el tipo de la variable; por ejemplo, int a, entonces cout<<typeid(a).name() imprimirá int template<class T> A<T>::A(){cout<<"class A goucao"<<typeid(T).name()<<endl;} //Método para definir el constructor de la clase fuera de la plantilla de clase template<class T> T A<T>::g(T a,T b){cout<<"class A g(T a,T b)"<<endl;} //Definir los miembros de la plantilla de clase fuera de la plantilla de clase template<class T1,class T2> voidB<T1,T2>::g(){cout<<"class g f()"<<typeid(T1).name()<<typeid(T2).name()<<endl;} //Al definir los miembros de la clase fuera de la clase, los parámetros de plantilla después de template deben ser coherentes con los parámetros de plantilla de la clase que se va a definir template<class T1,int a> voidCi<T1,a>::g(){cout<<"class Ci g()"<<typeid(T1).name()<<endl;} template<class T1,int &a> voidCip<T1,a>::g(){cout<<"class Cip g()"<<typeid(T1).name()<<endl;} //Al definir los miembros de la clase fuera de la clase, los parámetros de plantilla después de template deben coincidir con los parámetros de plantilla de la clase que se va a definir template<class T1,A<int> *m> voidCc<T1,m>::g(){cout<<"class Cc g()"<<typeid(T1).name()<<endl;} template<class T1,double* a> voidCd<T1,a>::g(){cout<<"class Cd g()"<<typeid(T1).name()<<endl;} //Método para definir miembros fuera de la clase en una plantilla de clase con parámetros de tipo predeterminados //Al definir los miembros de la clase fuera de la clase, los valores predeterminados en la lista de parámetros de template deben omitirse template<class T1,class T2> voidD<T1,T2>::g(){cout<<"class D g()"<<endl;} //template<class T1,class T2=int> void D<T1,T2>::g(){cout<<"class D k()"<<endl;} //error, al definir el parámetro con tipo predeterminado fuera de la plantilla de clasecuando, entemplatela lista de parámetros de la plantilla, el valor predeterminado debe omitirse.//Definir algunas variables globales. int e=2; doubleed=2.2; double*pe=&ed; A<int> mw; A<int> *pec=&mw; E me; //Comienza la función main int main() { // template<class T>void h(){} //error, la declaración o definición de una plantilla solo puede realizarse en el ámbito global, de espacio de nombres o de clase. Es decir, no puede realizarse en un ámbito local ni dentro de una función. //A<2> m; //error, para las plantillas de clase no existe deducción de argumentos; el tipo debe especificarse explícitamente entre corchetes angulares. //Ejemplo de invocación de una plantilla de clase A<int> ma; //Imprime "class A goucao int" y crea el objeto ma de la plantilla de clase A de tipo int. B<int,int> mb; mb.g(); //Imprime "class B g() int int" y crea el objeto mb de la plantilla de clase B, estableciendo los parámetros de tipo T1 y T2 como int. //Invocación de parámetros no tipo //El argumento utilizado para invocar un parámetro de plantilla no tipo debe ser una expresión constante, es decir, debe poder calcularse en tiempo de compilación. Cualquier objeto local, variable local, dirección de objeto local, dirección de variablelocal no es una expresión constante, ni puede usarse como argumento de un parámetro de plantilla no tipo. Los tipos de punteros globales, las variables globales y los objetos globales tampoco son expresiones constantes, y no pueden usarse como argumentos de parámetros de plantilla no tipo.//La dirección o referencia de una variable global, la dirección o referencia de un objeto global, o una variable de tipo const son expresiones constantes, y pueden usarse como argumentos de parámetros de plantilla no tipo. //El método para invocar un parámetro no tipo int se llama Ci; la forma de declaración es template<class T1,int a> class Ci Ci<int,3>//Correcto, el valor R es una constante de tipo int; imprime "class Ci g() int". const int a2=3; Ci<int,a2> mci1; mci1.g(); //Correcto, porque a2 aquí es una constante de tipo const; imprime "class Ci g() int". //Ci<int,a> mci; //error, la variable a de tipo int es una variable local, no una expresión constante. //Ci<int,e> mci; //error, la variable global e de tipo int tampoco es una expresión constante. //El método para invocar un parámetro no tipo int& se llama Cip; la forma de declaración es template<class T1,int &a>class Cip Cip<int,e> mcip; //Correcto, la referencia o la dirección de una variable global es una expresión constante. //Cip<int,a> mcip1; //error, la referencia o la dirección de una variable local no es una expresión constante. //El método para invocar un parámetro no tipo double* se llama Cd; la forma de declaración es template<class T1,double *a>class Cd Cd<int,&ed> mcd; //Correcto, la referencia o la dirección de una variable global es una expresión constante. //Cd<int,pe> mcd1; //error, el puntero a una variable global no es una expresión constante. //double dd=3.3; //error, la dirección de una variable local no es una expresión constante y no puede usarse como argumento de un parámetro no tipo. //Cd<int,&e> mcd; //error, aunque los parámetros no tipo permiten algunas conversiones, esta conversión no se puede realizar. //El método para invocar un parámetro de tipo plantilla que es un puntero a un objeto A<int> se llama Cc; la forma de declaración es template<class T1,A<int>* m> class Cc Cc<int,&mw> mcc; mcc.g(); //Correcto, la dirección o referencia de un objeto global es una expresión constante. //Cc<int,&ma> mcc; //error, la dirección o referencia de una variable local no es una expresión constante. //Cc<int,pec> mcc2; //error, el puntero a un objeto global no es una expresión constante. //El método para invocar la referencia de un objeto de parámetro no tipo E& se llama Ce. La forma de declaración es template<class T1,E &m> class Ce E me1; //Ce<int,me1> mce1; //error, un objeto local no es una expresión constante. Ce<int,me> mce; //Correcto, el puntero o la referencia de un objeto global es una expresión constante. //Ejemplo de conversión de parámetros no tipo, clase llamada Ci //Los parámetros no tipo permiten conversiones de array a puntero, de función a puntero, conversiones de calificador const, conversiones de promoción, conversiones enteras y conversiones habituales. const short s=3; Ci<int,s> mci4†//Correcto, aunque el tipo short y el tipo int no coinciden exactamente, aquí se puede convertir el tipo short al tipo int.

Dirección original:

https://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html

http://www.cnblogs.com/gw811/archive/2012/10/25/2736224.html