# 纲要
> 主干纲要、Hint/线索/路标
- **别名模版**
- **函数模版**
- **类模版**
- **成员模版**
%%
# Q&A
#### 已明确
#### 待明确
> 当下仍存有的疑惑
**❓<font color="#c0504d"> 有什么问题?</font>**
%%
<br>
# 别名模版 Alias Templates
通过 `using` 声明为一个 "**==模版化类型==**" 指定别名的方式,称为 "**别名模版**"。
```cpp template_alias.cpp
/* 模版别名
* 可使用`using`来声明一个"模版化类型", 称之为"别名模版".
* 由于模版不是"类型type", 因此不能使用`typedef`来声明模版别名.
*/
template <typename T> using Vec = std::vector<T>;
template <typename T> using twin = pair<T, T>;
template <typename T> using pairNo = pair<T, unsigned>;
int main() {
Vec<int> intVector; // 等价于`std::vector<int> intVector;`
twin<int> obj1_1(1, 2);
twin<char> obj1_2('A', 'B');
twin<double> obj1_3(3.14, 2.71);
pairNo<int> obj2_1(3, 4);
pairNo<char> obj2_2('C', 5);
pairNo<double> obj2_3(2.718, 3);
}
```
---
> [!caution] `typedef` 不能用于模版别名,因此 `C++98`中,要实现上述功能,需要采用 "将 `typedef` 嵌套进模版化的 `struct`" 来实现 "别名模版" 这一功能
>
> 参见下例[^1]。
>
```cpp
// 使用typedef来模拟 "别名模版" 的功能:
template <typename T>
struct Vec {
typedef std::vector<T> type;
}
Vec<int>::type intVector; // 等价于`std::vector<int> intVector`;
// 同时, `Vec<>::type` 作为依赖类型(依赖于模版参数)时, 还需要通过关键字`typename`显式指出
template<typename T>
class Widget {
private:
typename Vec<T>::type member; // 注意, `typename` 指示 `Vec<T>::type` 是一个类型名
}
```
<br><br>
# 函数模版 Function Templates
函数模板是**使用模板参数**来泛型定义函数的方式,支持同一函数实现对不同的数据类型进行操作。
> [!NOTE] "函数模版" 与 "模版函数" 的区别
>
> - **函数模版**(Funtion Template):是"模版",是基于模版参数来泛型定义的一个函数体
>
> - **模版函数**(Function Generated from a Template):是 "函数模版" 经实例化得到的**具体函数**。
>
函数模版可以声明为 `inline` 或 `constexpr` 的,这两关键字放在**模版参数列表之后,函数返回类型之前**。
```cpp
template <typename T> inline T min(const T&, const T&); // `inline`
template <typename T> constexpr T max(cosnt T&, const T&); // `constexpr`
```
<br><br>
# 类模版 Class Templates
类模版是使用模版参数来泛型定义一个类的方式,支持类对不同的数据类型进行操作。
类模版在**被使用时(隐式实例化)必须提供模版实参**。编译器不能为**类模版**推断模版参数类型。
> [!NOTE] "类模版" 与 "模版类" 的区别:
>
> - **类模版**(Class Template):是"模版",是基于模版参数来泛型定义的一个类。
> - **模版类**(Class Generated from a Template):是 "类模版" 经实例化得到的一个具体类。
<br>
## 类模版名的使用
> "**类模版名**" 不是一个**类型名**(type name),**一个实例化的 "==模版类的名称==" 包含==模版实参**==。参见 [^2]
> [!NOTE] 类模版的 **=="构造函数名"== 不能带有模版参数**,无论在类模版内外。
###### 在类模版作用域内使用类模版名
在**类模版作用域的==内部==**,使用 **"类模版名"** 时**可以==省略模版参数==**。<br>此时 **"类模版名"即代表"当前实例化的模版类"**,编译器会根据上下文推导出正确的模版参数。
例如:
- 在类模版==内部==**声明数据成员**时;
- 在类模版==内部==**声明或定义成员函数**时,函数的**返回类型、形参列表**、以及**函数体内**。
- 在类模版==外部==**定义成员函数**时,函数的**形参列表**中
###### 在类模版作用域外使用类模版名
在**类模版作用域的==外部==** ,使用"**类模版名**"时 **必须==提供模版参数==**,包括:
- 用于**类作用域标识符**时;
- **类模版外定义**类模版的成员函数时,函数的**返回类型**中;
- 类模版的友元函数中,函数返回类型、函数参数中;
说明示例:
```cpp
// 类模版作用域内部, 使用模版名时可省略"模版参数"
template <typename T>
struct MyClass {
MyClass();
MyClass(const MyClass&);
MyClass& operator=(const MyClass&);
};
// 类模版外定义其成员时, 使用"类模版名"必须提供模版参数.
// 自编译器见到"类模版作用域声明"起, 便进入了类模版作用域.
// 故"返回类型"中需提供完整类模版名, 而"形参列表"中不需要;
template<typename T>
MyClass<T>::MyClass() {}
template<typename T>
MyClass<T>::MyClass(const MyClass& rhs) {}
template<typename T>
MyClass<T>& MyClass<T>::operator=(const MyClass& rhs) {}
```
<br>
## 类模版成员的声明与定义
在**类模版外**定义类中成员时(成员函数定义、静态数据成员初始化等), 定义前需要**使用 "模版声明"**(`template` 关键字 + 模版参数列表)。
```cpp title:use_template_class_name.cpp
// 定义"类模版"
// 在类内部声明或定义成员时, 可以仅使用类模版名而不带模版参数.
// 这一情况下, "类模版名"即代表"当前实例化的模版类型", 编译器会根据上下文推导出正确的模版参数.
template <typename T>
struct MyClass { // 这里的`MyClass` 是一个"类模版名".
T content;
MyClass *p;
static T stc_content;
MyClass(T content); // 在类模版内, 构造函数名可以带有模版参数, 为`MyClass<T>(T content)`, 但通常省略.
MyClass(const MyClass& rhs);
MyClass& operator=(const MyClass& rhs);
};
// 类外定义类模版成员时:
// 1. 定义前必须带有"模版声明"
// 2. 引用类模版名时:
// 类作用域标识符中,类模版名"必须带有"模版参数;
// 成员函数的"返回类型"中使用类模版名,必须带有模版参数。
// 成员函数的"形参列表"中使用类模版名,可以省略模版参数。
template <typename T>
T MyClass<T>::stc_content = 25; // 初始化类模版中的静态成员变量
template <typename T>
MyClass<T>::MyClass(T content) {
this->content = content;
}
template <typename T>
MyClass<T>::MyClass(const MyClass<T>& rhs) { // `const MyClass<T>&`可省略为`const MyClass&`
content = rhs.content;
}
template <typename T>
MyClass<T>& MyClass<T>::operator=(const MyClass<T>& rhs) {
if (this != &rhs) {
content = rhs.content;
}
return *this;
}
```
<br>
## 类模版的成员函数
类模版的成员函数可以**在类内(为内联)或类外定义(==必须与类定义放在同一个头文件中==)**。
类模版的**成员函数默认==只有在被使用到时才进行实例化==**。
(因此允许成员函数为一个"函数模版")
> [!caution] 为保证类模版被正确的实例化和链接,**类模版的成员函数的定义**必须与**类模版的定义**放在**同一个头文件**中。
>
> 因为模版在 C++中是 "编译时" 实例化的,而**编译器在实例化类模版时,必须要看到模版的完整定义才能生成模版实例的代码**。
>
> ![[02-开发笔记/01-cpp/预处理相关/cpp-头文件说明#^cpj1sg]]
>
在**类定义的内部**定义成员函数:
```cpp title:my_class_template.h
template<typename T>
struct MyClass {
void SomeFunc() {
// ...
}
};
```
在**类定义外部、类定义所在的头文件中**定义成员函数:
```cpp title:my_class_template.h
template<typaname T>
struct MyClass {
void SomeFunc();
};
// 在类定义外部, 类定义所在的头文件中定义成员函数。
// 需要在定义前进行"模版声明"
template<typename T>
void MyClass<T>::SomeFunc() { // 指明`MyClass<T>`
// ...
}
```
## 类模版的静态成员
类模板的**每一个不同的实例化==模版类==**,**各自拥有一份独立的==静态数据成员==实例**。
```cpp title:static_member_in_class_template.cpp
template <typename T>
struct MyClass {
static T stc_content;
static int count;
};
// 例如, MyClass<int>::count 与 MyClass<double>::count是两个独立的静态数据成员。
template <typename T>
T MyClass<T>::stc_content = 25; // 初始化类模版中的静态成员变量
template <typename T>
int MyClass<T>::count = 0;
```
> [!NOTE] 对于类模版的静态成员函数,同样只有在**被使用时才会进行实例化**。
<br><br>
## 类模版与友元
当一个类包含一个友元声明时,==**类==与==友元==各自是否为模版相互无关**。
- 如果**类模版**包含一个 "**非模版友元**" (友元函数或友元类),则**友元被授权可以访问==所有该类模版的所有实例==**
- 如果**友元自身是模版**,则**类模版**可以授权给**所有友元模版实例**,也可以**只授权给特定实例**。
由此,对**类模版与其友元**可能构成以下关系:
- **==一对一==关系**:友元是一个**模版实例**,**依赖于==类模版的模版参数==** 进行**实例化**,则**每个类模版的具体实例**将对应有一个**模版实例友元**。
- **==多对多==关系**:友元是一个**模版**,则对于**该类模版的任意一个具体实例**,其 **==模版友元的所有实例==都是其友元**。
### 总结
在一个**类或类模版**中,其**友元**可以是:
- 一个常规友元(友元函数或友元类):友元可以访问**这个类或类模版所有实例**的私有成员
- **一个==模版"实例"**==(模版函数或模版类)作为友元:效果同上;
- **一个==模版==**(函数模版或类模版)作为友元:**该模版的==所有实例==** 都是这个类或类模版实例的**友元**
> [!tip] **"类模版"本身**可以作为其自己的 "**==模版实例友元==**" 或 "**==模版友元==**",表示**将其自身的==某个实例或其他所有实例==声明为友元**,从而允许**不同类型的实例互相访问私有成员**。
>
**类模版中,五种友元形式的总结说明**:
| 友元 | 说明 | |
| ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | --- |
| **普通友元** | 友元被授权**可访问该类模版的所有实例类** | |
| **模版实例**(该实例接受所属类的模版参数 T) <br>- `friend void Somefunc<T>();` <br>- `friend class OtherClass<T>;` | 由**模版参数 `T` 实例化**的特定友元,可以访问 "**同由模版参数 `T` 实例化**"的类<br><br> | |
| **模版实例**(该实例由具体参数实例化)<br>- `friend void func<int>();` <br>- `friend class OtherClass<int>;` | 该**特定实例**可访问该类模版的所有实例类 | |
| **模版友元**<br>- `template <typename U> void func(); ` <br>- `template <typename U> class OtherClass`; | **模版友元的所有实例,均可访问该类模版的任何实例**。 | |
| **类模版自身实例** or **类模版自身** <br>- `friend class MyClass<int>;` <br>- `template <typename U> friend class MyClass;` | 该类模版的任何实例,可被**该类模版自身的某一特定实例** or **其他任何实例**进行访问 | |
| **类模版的模版类型参数** <br>- `friend T;` | 该类型模版参数 T,可以访问该类模版的任何实例。 | |
示例:
> [!caution] 友元是 "模版实例" 时,需要有该 "模版" 的前置声明;
```cpp title:three_kinds_friend_compare.cpp
// 类模版的前置声明
// 声明友元模版类`friend class TemplateClass<T>`所需
template <typename> class TemplateClass;
// 友元函数的前置声明
void SomeFunc();
// 函数模版的前置声明
// 声明友元模版函数`friend void TemplateFunc<>(const T&)`所需.
template <typename> class MyClass; // 由于模版函数依赖于模版类`MyClass`, 因此还需在其之前声明模版类.
template <typename T> void TemplateFunc(const MyClass<T>&);
template <typename T>
struct MyClass {
// 常规友元类
friend class OtherClass;
// 以模版类实例作为友元, 需要"类模版"的前置声明;
friend class TemplateClass<T>; // 该友元以T实例化, 则只能访问同以T实例化的模版类
friend class TemplateClass<int>; // 该友元以特定模版参数实例化, 则其能访问该类模版的任何实例.
// 以类模版作为友元
template <typename U>
friend class ClassTemplate;
// 以常规函数作为友元. 需要前置声明
friend void SomeFunc();
// 以模版函数实例作为友元, 需要"函数模版"的前置声明.
friend void TemplateFunc<>(const MyClass<T>&); // 该友元以T实例化, 则只能访问同以T实例化的模版类
friend void TemplateFunc<int>(const MyClass<int>&); // 该友元以特定模版参数实例化, 则其能访问该类模版的任何实例.
// 以函数模版作为友元
template<typename U>
friend void FuncTemplate(const MyClass<U>&);
// 以该类模版自身的特定实例作为友元.
friend class MyClass<int>;
// 以该类模版自身的任意实例作为友元 => 使得该类模版任意实例之间均互为友元, 可互相访问;
template <typename U>
friend class MyClass;
// 以其自身的类型模版参数作为友元.
friend T;
};
int main() {
MyClass<int> obj;
}
```
### 以 "模板实例" 作为友元
示例:"**模版实例**作为友元"(一对一关系)
```cpp
// 对`FriendFunc<T>`模版函数前置声明
template <typename> class MyClass; // 由于模版函数依赖于模版类`MyClass`, 因此还需在其之前声明模版类.
template <typename T> void FriendFunc(const MyClass<T>&); // 声明模版函数. (注意, 区别于"显式实例化")
// 对`OtherClass<T>`模版类的前置声明
template <typename> class FriendClass;
template <typename T>
struct MyClass { // `Myclass` 是一个模版类, 其包含一个"类模版"数据成员
// 1) "依赖于类模版中模版参数"的模版类友元.
// `FriendClase`是依赖于类模版参数`T`来实例化的"模版类"友元.
// 下列友元声明只是将一个"模版类"声明为友元,
// 因此在类模版外部, 类模版定义之前需要存在有对该模版类本身的"声明".
friend class FriendClass<T>;
// 2) "依赖于类模版中模版参数"的模版函数友元:
// `FriendFunc`是依赖于类模版参数`T`来实例化的"模版函数"友元.
// 因此该模版函数友元应当声明为`FriendFunc<T>`, 显式指出其模版参数. 否则编译器将认为是非模版函数.
// 模版函数`FriendFunc<T>`与其实参"模版类"`MyClass<T>`具有一对一的对应关系
// 由于下列友元声明是只是将一个"模版函数"实例声明为友元,
// 因此在类模版外部, 类模版定义之前存在有对该模版函数本身的"声明".
friend void FriendFunc<T>(const MyClass<T>&);
private:
int val = 11;
};
template<typename T>
struct FriendClass {
// 因此在类模版外部, 类模版定义之前需要存在有对该模版类本身的"声明".
void func(const MyClass<T>& obj) {
cout << "Invoke the friend class's function. The val of obj is : " << obj.val << "\n";
}
};
template <typename T>
void FriendFunc(const MyClass<T>& obj) {
cout << "Invoke the friend function. The val of obj is : " << obj.val << "\n";
}
int main() {
MyClass<int> obj_i;
MyClass<char> obj_ch;
// `FriendFunc<T>`与 `MyClass<T>` 具有一对一的对应关系
FriendFunc(obj_i); // 正确. 编译器根据函数实参推导函数模版参数类型.
FriendFunc<int>(obj_i); // 正确
// FriendFunc<char>(obj); // error: `FrienFunc<char>`与`MyClass<int>`不是友元关系
FriendFunc(obj_ch); // 正确. 编译器根据函数实参推导函数模版参数类型.
FriendFunc<char>(obj_ch); // 正确
// FriendFunc<int>(obj_ch); // error: `FrienFunc<int>`与`MyClass<char>`不是友元关系
// `FriendClass<T>`与 `MyClass<T>` 具有一对一的对应关系
FriendClass<int> f_obj_i;
FriendClass<char> f_obj_ch;
f_obj_i.func(obj_i); // 正确
// f_obj_i.func(obj_ch); // error: `FriendClass<int>`与`MyClass<char>`不是友元关系
f_obj_ch.func(obj_ch); // 正确
// f_obj_ch.func(obj_i); // error: `FriendClass<char>`与`MyClass<int>`不是友元关系
}
```
> [!caution] 声明 "模板函数实例" 时,必须在函数名后带有 "**模板实参列表 `<>`**",但**可省略具体的模板实参名**。
> 如果未带有实参列表,则编译器认为是声明一个"普通函数" 作为友元。
示例:
```cpp
template <typename T> void TemplateFunc(const T& obj);
template <typename T>
struct MyClass {
// 声明一个"模板函数实例"作为友元
// 省略了模板实参名`T`, 但必须保留模板参数列表`<>`
friend void TemplateFunc<>(const T& obj);
// friend void TemplateFunc<T>(const T& obj); // 等价于上语句
// 声明一个"普通函数"作为友元
// 注意区别, 此处声明的NotTemplateFunc不是一个"模板函数实例", 而是能接收该类模板实例的普通函数.
friend void NotTemplateFunc(const T& obj);
};
// 普通函数
void NotTemplateFunc(const MyClass<int>& obj) {
// ...
}
// 函数模板定义
template <typename T>
void TemplateFunc(const T& obj) {
// ...
}
int main() {
MyClass<int> obj_i;
MyClass<char> obj_ch;
NotTemplateFunc(obj_i);
// NotTemplateFunc(obj_ch); // error: 未定义能接收`MyClass<char>`NotTemplateFunc的重载版本
TemplateFunc(obj_i);
TemplateFunc(obj_ch);
return 0;
}
```
### 以 "模板" 作为友元
示例:"**模版**作为友元"(多对多关系)
```cpp title:template_as_friend.cpp
// 在类或类模版中, 以"模版"(类模版或函数模版)作为友元.
// 则"模版"友元的所有实例都是这个类(一对多)或这一类模版所有实例(多对多)的友元
struct NormalClass {
// 类模版友元
template <typename U, typename V> friend class Printer;
// 函数模版友元
template <typename U, int>
friend void Add(const U&, const U&);
private:
int val = 11;
};
template <typename T>
struct TemplateClass {
// 类模版友元
template <typename U, typename V> friend class Printer;
// 函数模版友元
template <typename U, int>
friend void Add(const U&, const U&);
private:
int val = 22;
T content;
};
template <typename T, typename V>
struct Printer {
void func(const T& obj, V val) {
cout << "Invoke FriendClass::func(). "
<< " val in obj: " << obj.val << "; "
<< " val in param: " << val << endl;
}
};
template <typename T, int init>
void Add(const T& lhs, const T& rhs) {
cout << "Invoke Add(): "
<< lhs.val + rhs.val + init << endl;
}
int main() {
NormalClass nor_obj;
TemplateClass<int> tem_obj_i;
TemplateClass<char> tem_obj_ch;
TemplateClass<string> tem_obj_str;
// 函数模版`Add`是`NormalClass`和`TemplateClass`的友元
// 因此`Add`的所有实例都是`NormalClass`和`TemplateClass`的友元
Add<NormalClass, 10>(nor_obj, nor_obj);
Add<NormalClass, 20>(nor_obj, nor_obj);
Add<TemplateClass<int>, 10>(tem_obj_i, tem_obj_i);
Add<TemplateClass<int>, 20>(tem_obj_i, tem_obj_i);
Add<TemplateClass<char>, 55>(tem_obj_ch, tem_obj_ch);
Add<TemplateClass<string>, 100>(tem_obj_str, tem_obj_str);
// 类模版`Printer`是`NormalClass`和`TemplateClass`的友元
// 因此`Printer`的所有实例都是`NormalClass`和`TemplateClass`的友元
Printer<NormalClass, int> f_nor_obj_i;
f_nor_obj_i.func(nor_obj, 123);
Printer<NormalClass, char> f_nor_obj_ch;
f_nor_obj_ch.func(nor_obj, 'A');
Printer<TemplateClass<int>, int> f_tem_i_obj_i;
f_tem_i_obj_i.func(tem_obj_i, 123);
Printer<TemplateClass<int>, char> f_tem_i_obj_ch;
f_tem_i_obj_ch.func(tem_obj_i, 'D');
Printer<TemplateClass<char>, char> f_tem_ch_obj_ch;
f_tem_ch_obj_ch.func(tem_obj_ch, 'E');
Printer<TemplateClass<string>, char> f_tem_str_obj_ch;
f_tem_str_obj_ch.func(tem_obj_str, 'F');
}
```
### 让"类模版"的所有实例互相作为彼此的友元
示例:以 **"类模版"自身的其他实例** 作为其自己的友元
```cpp title:all_instances_of_a_class_template_as_friends_with_each_other.cpp
<typename T>
class MyClass {
// 所有`MyClass`类模版的实例都是彼此的友元.
// 从而允许不同类型的实例互相访问私有成员
template <typename U> friend class MyClass;
public:
MyClass(T val) : data(val){ }
// 模版函数, 用于访问另一个MyClass模版实例的私有成员
template <typename U>
void func(const MyClass<U>& obj) {
cout << "Accessing friend's private data: " << obj.data << "\n";
}
private:
T data;
};
int main() {
MyClass<int> obj_i(154);
MyClass<char> obj_ch('G');
MyClass<string> obj_str("Hello");
// MyClass的所有实例都是彼此的友元, 因此可以互相访问私有成员
obj_i.func(obj_ch);
obj_i.func(obj_str);
obj_ch.func(obj_i);
obj_ch.func(obj_str);
obj_str.func(obj_i);
obj_str.func(obj_i);
}
```
<br><br><br>
# 成员模版 Member Templates
"**成员模板**"(Member Templates)是定义在类或类模版内部的 「**==成员函数模版==**」[^3]。
> [!NOTE] 类模版的**成员函数默认==只有在被使用到时才进行实例化==**,因此成员函数可以是一个 "函数模版"。
成员模板允许**函数接受或返回泛型类型**,使得类能够**以通用的方式处理不同的数据类型**。
在一个**普通类或类模版**中:
- 其**数据成员**可以是**一个==实例化的模版类**==(例如实例化的容器类)
- 其**成员函数**(除虚函数)可以是一个 "==**函数模版**==",即称之为 "**==成员模版==**"。
- 其**内部**可以 **==嵌套==** 一个 "==**类模版**==";
> [!caution] **成员模版不能是==虚函数==,可以是除虚函数以外的任何成员函数**,包括构造函数等。
> [!NOTE] **类模版**与其**成员模版**具有**各自独立的模版参数**。
> 当在**类模版外**为**成员模版**进行定义时,**必须同时为类模版和成员模版提供模版参数列表**
> (各自使用独立的 `template <...>` 模版声明语句——类模版的模版参数列表在前,成员模版的模版参数列表在后)。
类模版中的成员模版说明实例:
```cpp
template <typename T>
struct MyClass { // `Myclass` 是一个模版类
// 类模版中的"成员函数"可以是"函数模版", 即为"成员模版"
template <typename U>
void MemberFunc(U arg);
template <typename U>
static void StaticMemberFunc(U arg);
};
// 在类模版外定义类模版中的"成员函数模版".
// 必须同时提供"类模版的模版参数", 以及"成员模版的模版参数". 二者使用独立的语句.
template <typename T> // T和U不能放在同一个模版参数列表中, 因为是属于不同的模版的参数.
template <typename U>
void MyClass<T>::MemberFunc(U arg) {
cout << arg << endl;
}
template <typename T>
template <typename U>
void MyClass<T>::StaticMemberFunc(U arg) {
cout << arg << endl;
}
```
<br><br>
## 成员模版的使用场景
成员模版的常见使用场景包括:
- **泛型算法实现**:例如一个类可能提供一个泛型排序函数,对任意类型元素数组排序。
- **泛型数据结构**:例如一个类可能需要内部使用一个**泛型栈或队列**来存储数据。
具体来说,示例有:
- **泛型构造函数**和**泛型赋值操作**
- **容器类**的**通用插入和获取操作**
- **类型转换操作**:将类类型转化为其他多种泛型
- **回调函数和委托**:使用成员模板来**注册和调用不同签名的回调函数**,这**在实现事件处理系统或委托调用时特别有用**。
### 泛型构造函数和赋值操作
###### 使用示例:**泛型构造函数**
**构造函数成员模版**可称之为"**泛型构造函数**",提供了用于“**对象被复制时给予隐式类型转换**”的能力。
泛型构造函数**并不会压制对 "拷贝或移动构造函数" 的调用**,无论是在**显式类型转换还是隐式类型转换**中,如果**源类型与拷贝/移动构造函数的==参数类型完全吻合==,则他们将被调用**。
```cpp
template <typename T>
class MyClass {
public:
// template构造函数: 具有隐式类型转换的复制构造函数
template <typename U>
MyClass(const Myclass<U> &x);
// 复制构造函数
MyClass(const Myclass<T> &x);
...
};
void f() {
MyClass<double> xd;
...
MyClass<double> xd2(xd); // calls implicitly generated copy constructor
MyClass<int> xi(xd); // calls template constructor
}
```
示例二:
```cpp
template <typename T>
class MyClass {
public:
MyClass() : ptr{} {}
// 模版构造函数, 可接受不同类型的迭代器
template <typename It> MyClass(It b, It e);
private:
std::shared_ptr<std::vector<T>> ptr;
};
template <typename T>
template <typename It>
MyClass<T>::MyClass(It b, It e): ptr(std::make_shared<std::vector<T>>(b, e)) { }
int main() {
int arr[] = { 0, 1, 2, 3, 4, 5 };
vector<long> vi = { 99, 98, 97, 96, 95 };
list<const char*> w = { "now", "is", "the", "time" };
// 将实例化一个模版构造函数MyClass<int>::MyClass(int*, int*);
MyClass<int> a1(begin(arr), end(arr));
// 将实例化一个模版构造函数MyClass<int>::MyClass(vector<long>::iterator, vector<long>::iterator);
MyClass<int> a2(vi.begin(), vi.end());
// 将实例化一个模版构造函数MyClass<int>::MyClass(list<const char*>::iterator, list<const char*>::iterator);
MyClass<string> a3(w.begin(), w.end());
return 0;
}
```
###### 使用示例:实现**类模版不同实例之间的互相赋值**
```cpp
template <typename T>
class MyClass {
public:
template <typename U>
MyClass<T>& operator=(const MyClass<U>& obj) {
// 这里不需要再特别处理自赋值的情况, 能够包含.
// 并且`this == &obj`的比较是编译器不允许的, 因为二者类型不同.
cout << "Assigning from " << typeid(obj).name() << " to " << typeid(*this).name() << endl;
// 形参`obj`的类型是`MyClass<U>`, 而`this`的类型是`MyClass<T>`, 二者类型不同.
// 因此不可以直接取`obj`的私有成员`value`, 需要通过`getValue`函数来获取.
value = static_cast<T>(obj.getValue());
return *this;
}
T getValue() const {
return value;
}
private:
T value;
};
int main() {
MyClass<int> obj_i;
MyClass<double> obj_d;
obj_i = obj_d;
return 0;
}
```
### 容器类的通用插入和获取操作
```cpp
template <typename T>
class Container {
public:
// 插入不同类型的数据
template <typename U>
void insert(const U& data) {
// 插入逻辑
}
// 从另一个容器获取数据
template <typename U>
void getFrom(const Container<U>& other) {
// 获取逻辑
}
};
```
### 类型转换操作
```cpp
template <typename T>
class Numeric {
public:
// 转换为不同的数值类型
template <typename U>
U toType() const {
return static_cast<U>(value);
}
private:
T value;
};
```
### 回调函数和委托
```cpp
#include <functional>
class EventDispatcher {
public:
// 注册具有任意签名的回调函数
template <typename Callback>
void registerEvent(std::string eventName, Callback cb) {
// 注册逻辑
}
// 触发事件
void triggerEvent(std::string eventName) {
// 触发逻辑,调用对应的回调函数
}
};
```
<br><br>
# 变量模版
since C++14。
例如,下图中声明的即为 "**变量模版**"。
![[_attachment/02-开发笔记/01-cpp/模版与泛型编程/cpp-模版类别.assets/IMG-cpp-模版类别-C7420CFD18257CE64C407205E1F45BA2.png|473]]
<br><br>
# Buffer
## 闪念
> sudden idea
## 候选资料
> Read it later
# ♾️参考资料
# Footnotes
[^1]: 《Effective Modern C++》Item9
[^2]: 《C++Primer》P588
[^3]: 《C++Primer》P595
[^5]: 《C++Primer》P607