C++ Nuevo clásico | C++ Comprobación de espacios faltantes y llenado (plantilla y genéricos)

Tabla de contenido

1. Plantilla de funciones

1. Definición de plantilla de función

2. Parámetros de plantilla sin tipo

2. Plantilla de clase

1. Función miembro de la plantilla de clase

2. Uso de nombres de plantillas de clases

3. El papel del nombre de tipo

1. El papel de los operadores de alcance

2.El papel del nombre de tipo

4. Parámetros de plantilla predeterminados

1. Plantilla de clase

2. Plantilla de función

5. Plantillas de funciones miembro de clases ordinarias.

6. Plantilla de función miembro de plantilla de clase

7. Creación de instancias explícitas y declaración de plantillas.

1. La plantilla de clase muestra la definición y declaración de creación de instancias.

2. La plantilla de función muestra la definición y declaración de creación de instancias.

3.Otros

八、typedef y uso

1.typedef

2.usando

(1) el uso define el alias de la plantilla

(2) el uso define tipos comunes

(3) usando define el puntero de función

3. Resumen

9. Plantilla de especialización completa y especialización parcial.

1.Toda especialización

(1) Especialización total de plantillas de clases.

(2) Especialización total de plantillas de funciones.

2. Especialización parcial

(1) Especialización parcial en el rango de parámetros de la plantilla


        Las plantillas generalmente se dividen en plantillas de funciones y plantillas de clases. Las plantillas solo se crean instancias cuando se utilizan.

1. Plantilla de funciones

1. Definición de plantilla de función

template <typename T>    //T为类型参数
T Add(T a, T b)
{
    return a + b;
}

        El código anterior define una plantilla de función (también llamada función de plantilla), que equivale a definir una fórmula o equivale a definir una plantilla.

  • La definición de una plantilla comienza con la palabra clave de plantilla, seguida de corchetes angulares. Dentro de los corchetes angulares está la lista de parámetros de plantilla . Si hay varios parámetros de plantilla aquí, sepárelos con comas. Debe haber al menos un parámetro de plantilla en el ángulo. soportes. Hay una palabra clave de nombre de tipo antes del parámetro de plantilla, que se puede escribir como nombre de tipo o clase (la clase aquí obviamente no se usa para definir una clase). Si hay varios parámetros de plantilla aquí, debe escribir varios nombres de tipo o clases. Incluso si hay varios parámetros de plantilla, puede mezclar nombre de tipo y clase. Sin embargo, en términos generales, el nombre de tipo se usa más comúnmente.
  • La lista de parámetros de plantilla representa el "tipo" o "valor" utilizado en la definición de función. También es similar a la lista de parámetros de función. Cuando la usa, a veces debe especificar los parámetros reales de la plantilla. Al especificar, también debe usar "<>" para implementar la plantilla. Envuelva el ginseng. A veces no es necesario especificar los parámetros reales de la plantilla y el sistema puede inferirlos basándose en cierta información.
  • La función Agregar declara un parámetro de tipo llamado T. El compilador determinará el tipo de T en función de la llamada a Agregar durante la compilación.

        La definición de una plantilla de función no hace que el compilador genere código relevante. Solo cuando se llama a esta plantilla de función, el compilador creará una instancia de una versión específica de la función y generará código relacionado con la función.

        Cuando el compilador genera código, necesita poder encontrar la parte del cuerpo de la función de la plantilla de función, por lo que la definición de la plantilla de función suele estar en el archivo de encabezado .h.

2. Parámetros de plantilla sin tipo

template <typename T> 

        La T anterior, debido a que se modificó con el nombre de tipo antes, por lo que T representa un tipo y es un parámetro de tipo.

        En esta lista de parámetros de plantilla, también puede definir parámetros que no sean de tipo. Los parámetros de tipo representan un tipo, mientras que los parámetros que no son de tipo representan un valor. Dado que el parámetro sin tipo representa un valor, por supuesto, no se puede modificar con nombre de tipo/clase, sino que se debe usar el nombre de tipo tradicional aprendido en el pasado para especificar el parámetro sin tipo, por ejemplo, el parámetro sin tipo s es un número entero, entonces se escribe como int s. Cuando se crea una instancia de la plantilla, los valores de dichos parámetros de plantilla que no son de tipo los proporciona el usuario o el compilador los infiere. Pero todos estos valores deben ser expresiones constantes, porque el compilador crea una instancia de estas plantillas en el momento de la compilación (solo las expresiones constantes pueden determinar el valor en el momento de la compilación).

        Para los parámetros de plantilla que no son de tipo, el tipo de parámetros todavía está sujeto a ciertas restricciones:

  • El argumento del parámetro de plantilla que no es de tipo debe ser una expresión constante; de ​​lo contrario, se producirá un error de compilación.
  • Los tipos de coma flotante generalmente no se pueden utilizar como parámetros de plantilla que no sean de tipo.
  • Los tipos de clase tampoco se pueden utilizar como parámetros de plantilla que no sean de tipo.
template <int a,int b>
int Add()
{
    return a + b;
}
int main()
{
    Add<10,20>();
    return 0;
}

2. Plantilla de clase

        En las plantillas de funciones, a veces es necesario proporcionar parámetros de plantilla y, a veces, el compilador infiere los parámetros de la plantilla por sí mismo.

        Pero las plantillas de clase son un poco diferentes: el compilador no puede inferir parámetros de plantilla para plantillas de clase. Por lo tanto, para utilizar una plantilla de clase, debe utilizar corchetes angulares "<>" después del nombre de la plantilla para proporcionar información adicional. Esta información en realidad corresponde a los parámetros en la lista de parámetros de la plantilla.

template <typename T>
class TEST {
public:

};

        Si hay varios parámetros de plantilla en "<>" después de la plantilla, los parámetros deben estar separados por comas. Las plantillas de clase también admiten parámetros de plantilla que no son de tipo.

        TEST es un nombre de plantilla de clase, no un nombre de tipo (o un nombre de tipo incompleto). Las plantillas de clase se utilizan para crear instancias de tipos. Entonces TEST<int>, TEST<double> o TEST<string> son los nombres de tipos reales (plantillas de clase instanciadas). Por lo tanto, se puede ver que un tipo de clase instanciado a través de una plantilla de clase siempre contiene parámetros de plantilla entre corchetes angulares.

1. Función miembro de la plantilla de clase

        Para las plantillas de clase, debido a que toda la información de la plantilla de clase debe estar disponible al crear una instancia de una clase específica, incluido el contenido específico del cuerpo de la función de las funciones miembro en la plantilla de clase, etc., por lo tanto, toda la información de la plantilla de clase , ya sea declaración o implementación, etc. Debe escribirse en un archivo .h. Otros necesitan usar los archivos de programa fuente de la plantilla de clase (como los archivos .cpp). Simplemente #incluya el archivo .h de esta clase plantilla.

        Las funciones miembro de las plantillas de clase tienen parámetros de plantilla:

  • Si la función miembro de esta plantilla de clase está definida en la plantilla de clase, los parámetros de plantilla de esta función miembro no se reflejarán.
  • Si la implementación de una función miembro de una plantilla de clase se escribe fuera de la definición de la plantilla de clase, entonces se reflejarán los parámetros de la plantilla de la función miembro. Es decir, las funciones miembro definidas fuera de la plantilla de clase deben comenzar con la plantilla de palabras clave, seguida de la lista de parámetros de la plantilla de clase. Al mismo tiempo, utilice corchetes angulares "<>" después del nombre de la clase para enumerar todos los nombres de los parámetros de la plantilla en la lista de parámetros de la plantilla. Si hay varios parámetros de plantilla, sepárelos con ",".
//.h文件
template <typename T>
class TEST {
public:
    void Test();
};

//.cpp文件
template<typename T>
void TEST<T>::Test()
{
}

        Aunque una plantilla de clase puede tener muchas funciones miembro, cuando se crea una instancia de la plantilla, si una determinada función miembro no se utiliza posteriormente, no se creará una instancia de esta función miembro. En otras palabras, una plantilla instanciada tiene instancias de sus miembros sólo cuando se utilizan.

        Las funciones de miembros de clases ordinarias solo se pueden usar dentro de un tipo de clase específico, se crea una instancia de su código de implementación durante la compilación y solo pueden manejar tipos específicos. Las funciones miembro de la plantilla de clase se pueden utilizar en cualquier tipo de instancia de plantilla. Su código de implementación se crea una instancia cuando se crea una instancia de la plantilla y puede manejar cualquier tipo.

2. Uso de nombres de plantillas de clases

        Dentro de una plantilla de clase, puede usar el nombre de la plantilla de clase directamente y no es necesario seguir el nombre de la plantilla de clase con parámetros de plantilla. Porque dentro de la definición de la plantilla de clase, si no se proporcionan parámetros de plantilla de clase, el compilador asumirá que el nombre de la plantilla de clase con y sin parámetros de plantilla es equivalente (es decir, TEST es equivalente a TEST<T>).

3. El papel del nombre de tipo

1. El papel de los operadores de alcance

  • Es necesario al acceder a variables miembro estáticas en una clase, es decir, nombre de clase:: nombre de variable miembro estática.
  • Se puede utilizar para indicar miembros de tipo.

2.El papel del nombre de tipo

  • En la definición de la plantilla, indica que los siguientes parámetros de la plantilla son parámetros de tipo. En este momento, la clase puede reemplazar el nombre de tipo.
  • Informa al compilador que un nombre representa un tipo. Pero tenga en cuenta que aquí el nombre de tipo no se puede reemplazar con clase.
//.h中
template <typename T>
class TEST {
public:
    typedef T* iter;

    iter GetNext();
};

//.cpp中
template<typename T>
typename TEST<T>::iter TEST<T>::GetNext()     //没有typename会报:语法错误: 标识符“iter”
{
    return iter();
}

        C ++ supone que se accede a las variables miembro estáticas a través del operador de alcance en lugar de los tipos, por lo que si el código anterior no se modifica con el nombre de tipo, el compilador informará un error. La solución es decirle explícitamente al compilador que iter es un tipo, así que modifíquelo con el nombre del tipo delante.

4. Parámetros de plantilla predeterminados

1. Plantilla de clase

        Si un parámetro de plantilla tiene un valor predeterminado, a partir de este parámetro de plantilla con un valor predeterminado, todos los parámetros de plantilla posteriores deben tener un valor predeterminado (esto es lo mismo que la regla de valor predeterminado para los parámetros de función). Al llamar, si usa el valor predeterminado por completo, puede usar directamente un corchete angular vacío (el corchete angular vacío no se puede omitir).

//.h文件
template <typename T=int>
class TEST {
public:
    void Test();
};


//.cpp文件
template<typename T>
void TEST<T>::Test()
{
}

int main()
{
    TEST<float> t_float;
    t_float.Test();
    TEST<> t_int;  //使用默认类型,<>不能省
    t_int.Test();
    return 0;
}

2. Plantilla de función

        El antiguo estándar C++ solo permitía proporcionar parámetros de plantilla predeterminados para plantillas de clase, y el nuevo estándar C++ 11 también puede proporcionar parámetros de plantilla predeterminados para plantillas de funciones.

template <typename T=int>
T Add(T a,T b)
{
    return a + b;
}

5. Plantillas de funciones miembro de clases ordinarias.

        Independientemente de si es una clase ordinaria o una plantilla de clase, su función miembro en sí misma puede ser una plantilla de función. Esta función miembro se denomina "plantilla de función miembro", pero esta plantilla de función miembro no puede ser una función virtual. Si escribe una plantilla de función virtual, el compilador informará un error.

6. Plantilla de función miembro de plantilla de clase

        Una plantilla de clase también puede definir plantillas de funciones miembro para ella. En este caso, la plantilla de clase y su plantilla de funciones miembro tienen parámetros de plantilla independientes.

//.h文件
template <typename T>
class TEST {
public:
    template <typename T1>
    void Test();           //类模板中的成员函数模板

    void Test2();          //类模板中的成员函数
};


//.cpp中
template<typename T>
template<typename T1>
void TEST<T>::Test()
{
}

template<typename T>
void TEST<T>::Test2()
{
}
  1. Las funciones miembro en una plantilla de clase aparecerán en una plantilla de clase instanciada solo si el código que llama a estas funciones miembro aparece en el código del programa fuente.
  2. Para las plantillas de funciones miembro en plantillas de clase, las instancias específicas de estas plantillas de funciones miembro aparecerán en una plantilla de clase instanciada solo cuando el código que llama a estas plantillas de funciones miembro aparece en el código del programa fuente.

7. Creación de instancias explícitas y declaración de plantillas.

        Las plantillas solo se crean instancias cuando se utilizan. Por lo tanto, si se utiliza la misma plantilla en dos archivos cpp diferentes, se crearán instancias de la plantilla dos veces. ( Esto se debe a que el modelo de compilación de C++ es compilar cada archivo fuente por separado, cada uno con su propio contexto de compilación. Cuando el compilador encuentra el uso de una plantilla en un archivo fuente, crea una instancia de la plantilla en ese contexto. Cuando encuentra el La misma plantilla se usa nuevamente en otro archivo fuente, se crea una instancia de la plantilla nuevamente ) .

        Esta sobrecarga de generar múltiples instancias de plantilla de la misma clase se puede evitar mediante la "creación de instancias explícitas". Solo hay una definición de creación de instancias de una plantilla y puede haber varias declaraciones de creación de instancias de una plantilla. No olvide escribir la definición de creación de instancias; de lo contrario, no logrará el efecto de reducir la sobrecarga adicional del sistema ni provocará errores de enlace.

        El formato de la definición de creación de instancias de plantilla comienza con la plantilla, mientras que el formato de la declaración de creación de instancias de plantilla comienza con la plantilla externa. Cuando el compilador encuentra una declaración de creación de instancias de plantilla externa, no generará un código de versión de creación de instancias de la plantilla de clase representada por extern en este archivo fuente .cpp. El significado de este externo es decirle al compilador que ya existe una versión instanciada de esta plantilla de clase en otros archivos fuente .cpp, por lo que este externo generalmente se escribe al comienzo de múltiples archivos fuente .cpp.

1. La plantilla de clase muestra la definición y declaración de creación de instancias.

//类模板
template <typename T>
class TEST {
public:
    void Test2();          //类模板中的成员函数
};

template<typename T>
void TEST<T>::Test2()
{
}

//_1.cpp中实例化定义
template TEST<float>;//实例化定义

//_2.cpp中声明
extern template TEST<float>;//实例化声明

2. La plantilla de función muestra la definición y declaración de creación de instancias.

template <typename T>
T Add(T a,T b)
{
    return a + b;
}

//_1.cpp
template int Add(int a, int b);//实例化定义
//_2.cpp
extern template int Add(int a, int b);//实例化声明

3.Otros

        Cuando se utiliza Visual Studio 2017 o Visual Studio 2019, no se recomienda utilizar la función de creación de instancias explícita de plantilla de clase, porque aunque esta característica es útil, también creará instancias de todas las funciones miembro, lo que aumentará el tiempo de compilación y la longitud del código.

八、typedef y uso

1.typedef

        El orden de definición de tipos en typedef es como definir una variable: después de typedef, primero escriba el nombre del tipo del sistema, luego siga con un espacio y luego siga con el alias de tipo que desea crear.

    typedef int INT;
    INT a = 10;

2.usando

(1) el uso define el alias de la plantilla

        Hemos visto muchos usos diferentes del uso en el pasado, como usarlo para exponer la función de la clase principal con el mismo nombre en la subclase y la subclase hereda el constructor de la clase principal. Hoy presentaremos otra función de uso: plantilla de alias.

//类模板
template <typename T>
class TestUsingClass {
public:
    void Test() {

    }
};

//using测试
template <typename T>
using MyClass = TestUsingClass<T>;
void test22()
{
    MyClass<int> myclass;
    myclass.Test();
}

(2) el uso define tipos comunes

        El uso también puede definir tipos ordinarios, pero en términos de sintaxis, es todo lo contrario del orden en que typedef define los tipos.

    typedef int INT;
    INT a = 10;

    using INT = int;
    INT a = 10;

(3) usando define el puntero de función

int Fun(int a, int b)
{
    return a * b;
}

typedef int(*MyFun)(int, int);

using MyFun = int(*)(int, int);
void Test(){
    MyFun myFun = &Fun;
    int result = myFun(1, 2);
    std::cout << result << std::endl;
}

3. Resumen

  • Usar el uso para definir plantillas relacionadas con tipos no es muy diferente de definir tipos ordinarios, excepto que se agrega una lista de parámetros de plantilla que comienza con plantilla al frente.
  • La plantilla utilizada al usar no es una plantilla de clase ni una plantilla de función, y puede considerarse como una nueva forma de plantilla: plantilla de alias.

9. Plantilla de especialización completa y especialización parcial.

        Al utilizar una plantilla, puede especificar cualquier parámetro de plantilla, lo que se denomina generalización (alcance más amplio).

        Escriba una plantilla de clase o una plantilla de función y pase un parámetro de plantilla de tipo. El tipo pasado puede especificarlo usted mismo, pero existe una situación en la que si se proporciona un tipo A, se puede crear una instancia de la plantilla normalmente, pero si se proporciona un tipo B, la plantilla no se puede crear una instancia normalmente, como un error de compilación. , etc. Las plantillas se denominan especializaciones.

        Siempre que se trata de especialización, primero debe existir la generalización. Si escribe directamente una versión especializada de una plantilla sin proporcionar una versión general (generalizada) correspondiente, el compilador informará un error. Porque la versión especializada es en realidad una redefinición o sobrescritura de un caso especial de la plantilla genérica. Si esta plantilla genérica no existe, no se puede especializar ni anular nada.

1.Toda especialización

(1) Especialización total de plantillas de clases.

        Especialización completa, es decir, todos los parámetros de la plantilla de tipos están representados por tipos específicos.

// 函数模板声明
template<typename T>
void foo(T t);

// 函数模板定义
template<typename T>
void foo(T t) {
    std::cout << "General template. Value: " << t << '\n';
}

// 函数模板全特化:对int类型进行特化
template<>
void foo<int>(int i) {
    std::cout << "Specialization for int. Value: " << i * 2 << '\n';
}

        plantilla <> indica que se trata de una especialización completa y va seguida de la definición de plantilla original que será reemplazada por la versión totalmente especializada.

(2) Especialización total de plantillas de funciones.

        La especialización completa de una plantilla de función es en realidad equivalente a crear una instancia de una plantilla de función y no es equivalente a una sobrecarga de función.

// 通用的函数模板
template <typename T>
void print(T value)
{
    std::cout << "This is the general template: " << value << std::endl;
}

// 对int类型进行全特化
template <>
void print<int>(int value)
{
    std::cout << "This is the specialization for int: " << value * 2 << std::endl;
}

//普通函数
void print(int value)
{
    std::cout << "This is a normal function: " << value * 2 << std::endl;
}

        Cuando se encuentra una llamada de función, es apropiado elegir una función ordinaria, también es apropiado seleccionar una plantilla de función (generalización) o es apropiado seleccionar una versión especializada de la plantilla de función. La prioridad del compilador es seleccionar la función ordinaria. función primero. Si no hay una función ordinaria, se considerará la versión especializada de la plantilla de función. Si no hay una versión especializada o la versión especializada no es adecuada, se considerará la versión generalizada de la plantilla de función.

2. Especialización parcial

        Especialización parcial, también llamada especialización local. Aquí hay dos aspectos: uno es la especialización parcial en el número de parámetros de la plantilla (esto es fácil de entender, es especialización parcial de los parámetros) y el otro es la especialización parcial en el rango de parámetros de la plantilla.

        Las plantillas de funciones no pueden especializarse parcialmente. Sólo las plantillas de clases pueden especializarse parcialmente.

(1) Especialización parcial en el rango de parámetros de la plantilla

        Especialización parcial en el rango de parámetros de la plantilla: por ejemplo, si originalmente es un tipo int y se convierte en un tipo int constante, ¿se reducirá el rango de este tipo? Para otro ejemplo, si originalmente era un tipo arbitrario T y ahora se convierte en T* (reducido de un tipo arbitrario a un tipo de puntero), ¡entonces este tipo también tiene un alcance más pequeño! También hay T& (referencia de valor l) y T&& (referencia de valor r). Para T, el rango de tipos se ha vuelto más pequeño.

Supongo que te gusta

Origin blog.csdn.net/weixin_39766005/article/details/132910961
Recomendado
Clasificación