做外贸电商网站有哪个,wordpress安装使用教程,基于django的电子商务网站设计,ceac网页设计师证书如何考目录
一#xff0c;模板 1#xff0c;函数模板
什么是函数模板
函数模板原理
函数模板的实例化 推演(隐式)实例化 显示实例化
模板的参数的匹配原则
2#xff0c;类模板
什么是类模板
类模板的实例化
二#xff0c;模板的特化
1#xff0c;类模板的特化
全特化… 目录
一模板 1函数模板
什么是函数模板
函数模板原理
函数模板的实例化 推演(隐式)实例化 显示实例化
模板的参数的匹配原则
2类模板
什么是类模板
类模板的实例化
二模板的特化
1类模板的特化
全特化 偏特化
2函数模板的特化——全特化
三非类型模板参数 一模板 1函数模板 什么是函数模板 所谓函数模板实际上是建立一个通用的函数该函数类型和形参类型不具体指定而是用一个表示任意类型的虚拟类型来代表(这里的任意类型可以任意选择如 T)。 定义函数模板的一般形式 template typename T1, typename T2, ....., typename Tn 返回值类型 函数名(参数列表) { // ..... } 其中template和class是关键字typename 可以用class关键字代替在这里typename 和class没区别括号中的参数叫模板形参模板形参和函数形参很相像模板形参不能为空。一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数即可以在该函数中使用内置类型的地方都可以使用模板形参名。模板形参需要调用该模板函数时提供的模板实参来初始化模板形参一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。比如swap的模板函数形式为 下面来看一下完成多个不同类型的两个数据的交换
针对具体类型常规函数
// 完成两个整形变量的交换
void Swap(int left, int right)
{int temp left;left right;right temp;
}// 完成两个double型变量的交换
void Swap(double left, double right)
{double temp left;left right;right temp;
}// 完成两个字符型变量的交换
void Swap(char left, char right)
{char temp left;left right;right temp;
}int main()
{int a 1, b 2;double c 1.1, d 2.2;char ch1 a, ch2 b;Swap(a, b);Swap(c, d);Swap(ch1, ch2);return 0;
}通过上面可以看出若想要完成以上三种不同类型的交换须要写三个交换函数这是针对每种类型分别写出具体的交换函数但是以上的三种交换函数除了参数的类型不一样其他都是一样的显得代码既冗余又不够简练。来看下面使用函数模板
跟具体类型无关(函数模板)
//函数模板
//templateclass T
templatetypename T // 声明一个类型模板参数 T
void Swap(T left, T right) // 使用模板参数T来声明函数参数left和right
{T temp left;left right;right temp;
}int main()
{int a 1, b 2;double c 1.1, d 2.2;char ch1 a, ch2 b;Swap(a, b);Swap(c, d);Swap(ch1, ch2);return 0;
}在这个例子中T 是一个类型模板参数它告诉编译器我们希望这个函数能够处理多种类型。在函数模板的声明中使用 typename 关键字也可以使用 class 关键字两者在函数模板中都是等价的来声明类型模板参数。 然后使用类型模板参数 T 来声明函数的参数 left和 right它们都是类型为 T 的引用。这意味着可以传递任何类型的变量给 swap 函数只要这两个变量的类型相同。 函数模板原理 当编译器遇到一个函数调用时编译器会尝试根据传递给函数模板的实参类型来推导出模板参数的类型一旦类型推导成功编译器就会生成一个或多个具体的函数实例这些实例的类型与推导出的模板参数类型相匹配。 注意函数模板本身并不产生代码它只是一个蓝图。只有在模板被实例化时编译器才会生成具体的函数代码。 当实参a、b 是 int 时编译器会把模板参数 T 推演成 int 类型会实例化出一份具体类型的Swap 函数来调用当实参a、b 是 double 时, 编译器会把模板参数 T 推演成 int 类型; char类型也一样。
注意以上三个函数在实例化时虽然走的都是同一个函数模板但是调用的不是同一个函数只是用同一个函数模板实例化出了三份针对具体类型的函数 如下 函数模板的实例化 推演(隐式)实例化 隐式实例化编译器在调用一个模板函数时根据提供的参数类型自动推断出模板参数的类型并生成相应的函数实例。这个过程是编译器自动完成的不需要程序员显式指定模板参数的类型。 // 声明一个函数模板
template typename T
T Add(T a, T b)
{return a b;
}int main()
{int a1 3, a2 5;double d1 3.5, d2 6.5;// 隐式实例化编译器根据参数类型int推断出模板参数T为intAdd(a1, a2); // 生成了 addint(int, int) 的实例 // 编译器根据参数类型double推断出模板参数T为doubleAdd(d1, d2); // 生成了 adddouble(double, double) 的实例 Add(a1, d1); // a1和d1是不同的类型此时编译报错编译器无法推演出具体的类型return 0;
}上述第三个 Add(a1, d1); 该语句不能通过编译因为在编译期间当编译器看到该实例化时需要推演其实参类型 通过实参a1将T推演为int通过实参d1将T推演为double类型但模板参数列表中只有一个T 编译器无法确定此处到底该将T确定为int 或者 double类型而报错 解决方法1. 可以改成多参数的函数模板 如templatetypename T1, typename T2 2. 手动强制类型转换 如上面的 Add(a1, (int)d1); 或 Add((double)a1, d1); 3. 就是下面要说的 显式实例化 显示实例化 函数模板允许编写通用的函数这些函数可以处理不同类型的数据。但是在某些情况下可能想要为特定的类型显式地实例化函数模板以便在编译时生成具体的函数版本。 即在函数名后的中指定模板参数的实际类型 // 声明一个函数模板
template typename T
T Add(T a, T b)
{return a b;
}int main()
{int a1 3, a2 5;double d1 3.5, d2 6.5;// Add(a1, d1); // a1和d1是不同的类型此时编译报错编译器无法推演出具体的类型Addint(a1, d1); // 显式实例化 可强制为 int 类型实例化并将参数 d1 强制转换为 int 类型// 或Adddouble(a1, d1); // 显式实例化 可强制为 double 类型实例化并将参数 a1 强制转换为 double 类型return 0;
}
上面的模板与函数调用 Add(a1, d1) 不匹配因为该模板要求两个函数参数的类型相同。但通过使用 Addint(a1, d1), 可强制为 int 类型实例化并将参数 d1 强制转换为 int 类型这样就可以与函数 Addint(int, int) 的第二个参数匹配。 模板的参数的匹配原则 1. 同时存在性一个非模板函数可以和一个同名的函数模板同时存在。此外这个函数模板还可以被实例化为这个非模板函数。 2. 优先调用非模板函数当非模板函数和同名函数模板在参数上相匹配时编译器会优先调用非模板函数而不是从模板产生出一个实例即有现成的就吃现成的 3. 模板的更好匹配如果模板可以产生一个具有更好匹配的函数那么编译器会选择模板而不是非模板函数即有更合适的就吃更合适的没有就将就吃。 // 函数模板可以处理任何类型的加法
template typename T
T Add(T a, T b)
{return a b;
}// 非模板函数专门处理int类型的加法
int Add(int a, int b)
{return a b;
}int main()
{int a1 3, a2 5;double d1 3.5, d2 6.5;// 调用非模板函数因为参数是int类型与非模板函数匹配 Addint(a1, a2); Adddouble(d1, d2); // 调用模板函数因为参数是double类型与非模板函数不匹配Adddouble(a1, d1); // 调用模板函数,模板函数会生成更匹配的因为参数是double类型与非模板函数不匹配 (a1会被强转成 double)return 0;
} 2类模板
什么是类模板 类模板是对一批成员数据类型不同的类的抽象。只需为这一批类所组成的整个类家族创建一个类模板并给出一套程序代码就可以用来生成多种具体的类这类可以看作是类模板的实例从而大大提高编程的效率。 类模板的基本结构如下 //template typename 参数名, typename ... // 可以有多个类型参数使用逗号分隔 template class T // 或使用typename代替class class 类名 { // 类的定义可以使用类型T作为成员变量、函数参数或返回类型 }; 其中template 关键字用于声明一个模板class 参数名, ... 部分定义了模板参数这些参数在模板内部可以用作类型。class关键字是用来指示随后的标识符是一个类型名称的但也可以使用 typename 关键字代替 class。 栈模板类可以定义如下
// 类模板
templateclass T
class Stack
{
public:Stack(int capacity 4){cout Stack(int capacity 4) endl;_a new T[capacity];_top 0;_capacity capacity;}~Stack(){cout ~Stack() endl;delete[] _a;_a nullptr;_top 0;_capacity 0;}
private:T* _a;int _top;int _capacity;
};
在上面的例子中定义了一个名为 Stack的类模板它接受一个类型参数 T。这个 T 类型被用作栈中元素的类型。 类模板的实例化
int main()
{// 显示实例化Stackint st1; // 实例化一份存储int 类型的栈Stackdouble st2; // 实例化一份存储double 类型的栈return 0;
} 在 main 函数中分别实例化了两个 Stack 对象一个用于存储整数Stackint另一个用于存储浮点数Stackdouble。这两个对象都使用了相同的 Stack 类模板但是它们内部处理的数据类型是不同的。这就是类模板的强大之处通过编写一次代码就可-以创建出多种类型安全的栈类。 注意1. 对于类模板模板形参的类型必须在类名后的尖括号中明确指定。比如A2 m;这种方法把模板形参设置为int是错误的类模板形参不存在实参推演的问题。也就是说不能把整型值2推演为int 型传递给模板形参。要把类模板形参调置为int 型必须这样指定Aint m。 2. 模板的声明或定义只能在全局命名空间或类范围内进行。即不能在局部范围函数内进行比如不能在main函数中声明或定义一个模板。 二模板的特化
1类模板的特化 类模板特化是针对特定类型的模板参数提供定制的类模板实现。它允许在某些情况下使用与通用模板不同的实现方式。类模板特化分为全特化和偏特化局部特化两种 全特化 对模板参数列表中的所有模板类型都进行具体化。例如如果有一个模板类TestT1, T2我们可以为T1和T2都是int的情况提供一个全特化的版本 templateclass T1, class T2
class Data
{
public:Data() { cout DataT1, T2 endl; }
};// 全特化为 int和char
template
class Dataint, char
{
public:Data() { cout Dataint, char endl; }
private:int _d1;char _d2;
};// 全特化为 int int
template
class Dataint, int
{
public:Data() { cout Dataint, int endl; }
private:
};int main()
{Dataint, int d1;Dataint, char d2;return 0;
}写一个类的全特化就相当于写一个新的类一样你可以自己定义任何东西不管是函数、数据成员、静态数据成员等等根据自己的需求 偏特化 偏特化就是如果这个模板有多个类型那么只限定其中的一部分即只对模板的部分类型进行明确指定 特化部分参数将模板参数类表中的一部分参数特化
有一个 Data的类模板它接受两个类型参数 T1和T2。T2为 int 的情况提供了偏特化
//原模版
templateclass T1, class T2
class Data
{
public:Data() { cout DataT1, T2 endl; }
};// 特化部分参数
template class T1
class DataT1, int
{
public:Data() { cout DataT1, int endl; }
};int main()
{Datachar, int d1; // 走特化版本 DataT1, intDataint, int d2;Dataint, char d3; // 走原模板第二个参数为char与第二个模板参数int不匹配return 0;
}
当T2是int时编译器将使用偏特化的Data类模板而不是原始的模板。如果T2不是int则编译器将使用原始的模板 对参数类型进行一定限制。比如限制是指针或者引用等
//对参数类型限制为指针
template class T1, class T2
class DataT1*, T2*
{
public:Data() { cout DataT1*, T2* endl; }
};//对参数类型限制为引用
template class T1, class T2
class DataT1, T2
{
public:Data() { cout DataT1, T2 endl; }
};//对参数类型T1限制为指针T2限制为引用
template class T1, class T2
class DataT1*, T2
{
public:Data() { cout DataT1*, T2 endl; }
};int main()
{Datachar*, int* d4; // 走特化版本 DataT1*, T2*Dataint*, int* d5;Dataint, int* d6; // 走原模板 Dataint, int d6; // 走特化版本 DataT1, T2Dataint*, int d7; // 走特化版本 DataT1*, T2return 0;
} 注意偏特化有一些限制。不能为一个非类型模板参数提供偏特化也不能为一个函数模板提供偏特化。同时偏特化的结果仍然是一个模板而不是一个具体的类 2函数模板的特化——全特化
//函数模板
templatetypename T1, typename T2
void fun(T1 a, T2 b)
{cout 函数模板 endl;
}//全特化
template
void funint, char (int a, char b)
{cout 全特化 endl;
}//函数不存在偏特化下面的代码是错误的
/*
templatetypename T2
void funchar, T2(char a, T2 b)
{cout 偏特化 endl;
}
*/注意对于函数模板只有全特化不能偏特化 三非类型模板参数 模板参数分为类型形参与非类型形参。 类型形参即出现在模板参数列表中跟在class或者typename之类的参数类型名称上面已经介绍过 非类型形参就是用一个常量作为类(函数)模板的一个参数在类(函数)模板中可将该参数当成常量来使用 // 定义一个模板类型的静态数组
templateclass T, size_t N
class Array
{
public: // ... 其他成员函数 ...private:T data[N];
}; int main()
{ Arrayint, 5 arr; // 创建一个大小为5的整型数组 // ...
} 注意 1. 非类型模板参数必须是常量表达式也就是说它们必须在编译时就能确定其值 2. 非类型模板参数的类型通常是整数类型如 int、size_t 等、指针(如 指向函数的指针)或引用但不能是类类型或其他复杂类型。 3. 非类型模板参数在模板实例化时会被替换为具体的值因此它们会影响生成的代码的类型和布局