类模板
设计class
的时候,如果数据的类型可以指定,那么就可以使用类模板。
template<typename T>
class complex
{
public:
complex(T r = 0, T i = 0)
: re(r), im(i)
{ }
complex& operator+=(const complex&);
T real() const { return re; }
T imag() const { return im; }
private:
T re, im;
friend complex& __doapl(complex*, const complex&);
};
{
complex<double> c1(2.5, 1.5);
complex<int> c2(2, 6);
}
1. 模板类
template<typename T>
:定义了一个模板类,T
是一个占位符类型,可以在实例化时替换为具体类型(如int
、double
等)。模板类支持泛型编程,提高代码的复用性。class complex
:定义了一个名为complex
的类,用于表示复数。
2. 构造函数
complex(T r = 0, T i = 0)
: re(r), im(i)
{ }
- 这是
complex
类的构造函数,用于初始化复数的实部re
和虚部im
。 - 参数
r
和i
有默认值0
,如果创建对象时不提供参数,实部和虚部将默认初始化为0
。 - 使用成员初始化列表(
: re(r), im(i)
)直接初始化成员变量,效率高于在构造函数体内赋值。
3. 成员函数
complex& operator+=(const complex&);
:- 重载了
+=
运算符,用于将另一个复数加到当前复数上。 - 返回
*this
,支持链式操作(如a += b += c
)。
- 重载了
T real() const { return re; }
和T imag() const { return im; }
:- 分别返回复数的实部
re
和虚部im
。 const
关键字表示这些函数不会修改对象的状态。
- 分别返回复数的实部
4. 私有成员变量
T re, im;
re
和im
分别存储复数的实部和虚部。- 声明为
private
,外部代码无法直接访问,只能通过公共成员函数访问,体现了封装性。
5. 友元函数
friend complex& __doapl(complex*, const complex&);
- 声明了一个友元函数
__doapl
,允许它访问complex
类的私有成员。 - 友元函数通常用于实现与类紧密相关但又不适合作为成员函数的功能。
6. 类实例化
complex<double> c1(2.5, 1.5);
complex<int> c2(2, 6);
complex<double> c1(2.5, 1.5);
:- 实例化一个
complex
对象c1
,模板参数为double
,实部为2.5
,虚部为1.5
。
- 实例化一个
complex<int> c2(2, 6);
:- 实例化一个
complex
对象c2
,模板参数为int
,实部为2
,虚部为6
。
- 实例化一个
C++中模板类、构造函数、成员函数、私有成员和友元函数的基本用法如下:
- 通过模板类,我们可以创建通用的复数类,支持不同类型的数据;
- 通过构造函数和成员函数,实现了复数的初始化和操作;
- 通过私有成员和友元函数,确保了类的封装性和灵活性。
函数模板
与上面的类模板一致,在设计函数的时候,如果传入的参数可以指定,那么就使用模板。在函数定义前面,写template<class T >
或者template<typename T>
// 函数模板声明
template <class T>
inline
const T& min(const T& a, const T& b)
{
return b < a? b : a;
}
// stone类的定义
class stone
{
public:
stone(int w, int h, int we) : _w(w), _h(h), _weight(we) {}
bool operator< (const stone& rhs) const
{
return _weight < rhs._weight;
}
private:
int _w, _h, _weight;
};
1. 函数模板
template <class T>
:这是函数模板的声明,template
是关键字,表示这是一个模板定义。<class T>
定义了一个类型参数T
,class
可以用typename
代替,二者在这里等价。inline const T& min(const T& a, const T& b)
:- 定义了一个模板函数
min
,接受两个const T&
类型的参数a
和b
,并返回较小的那个引用。 inline
关键字建议编译器将函数调用替换为函数体的代码,通常用于小型频繁调用的函数,以减少函数调用开销。
- 定义了一个模板函数
- 函数体:
return b < a? b : a;
:- 使用条件运算符
? :
比较a
和b
,并返回较小的值。 - 为了使这个操作正确执行,类型
T
必须支持<
运算符。
- 使用条件运算符
2. 类定义
class stone {... };
:定义了一个名为stone
的类。- 构造函数:
stone(int w, int h, int we) : _w(w), _h(h), _weight(we) {}
- 使用成员初始化列表初始化私有成员变量
_w
(宽度)、_h
(高度)和_weight
(重量)。
- 使用成员初始化列表初始化私有成员变量
- 重载运算符:
bool operator< (const stone& rhs) const { return _weight < rhs._weight; }
- 重载了
<
运算符,用于比较两个stone
对象的_weight
成员变量。 const
关键字表示该成员函数不会修改对象的状态。
- 重载了
3. 私有成员变量
int _w, _h, _weight;
_w
、_h
和_weight
分别存储stone
对象的宽度、高度和重量。- 声明为
private
,外部代码无法直接访问,体现了封装性。
4. 实参推导
- 当调用
min
函数模板时,例如r3 = min(r1, r2);
,编译器会根据传递的实际参数类型(假设r1
和r2
是stone
类型对象)自动推导出模板参数T
的具体类型。 - 因为传递的是
stone
对象,编译器会推导出T
为stone
,并调用stone::operator<
进行比较。 - 这就是实参推导的过程,它使得函数模板可以根据不同的实际类型生成特定的函数实例。
C++中函数模板和实参推导的基本用法如下:
- 通过函数模板,可以编写通用的
min
函数,支持不同类型的数据; - 通过实参推导,编译器能够自动推断模板参数类型,生成相应的函数实例。
成员模板
成员模板既是模板的一部分(在类中),自己又是模板,则称为成员模板。
template <class T1, class T2>
struct pair {
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair() : first(T1()), second(T2()) {}
pair(const T1& a, const T2& b) : first(a), second(b) {}
template <class U1, class U2>
pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
};
- 成员模板构造函数:
template <class U1, class U2> pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
- 这是一个成员模板构造函数,允许从另一个不同类型的
pair
对象构造当前pair
对象。 - 例如,可以从
pair<int, double>
构造pair<long, float>
,只要类型之间可以赋值。 - 成员模板增强了类的灵活性和通用性。
- 这是一个成员模板构造函数,允许从另一个不同类型的
// 基类和派生类定义
class Base1 {};
class Derived1 : public Base1 {};
class Base2 {};
class Derived2 : public Base2 {};
1. 什么是成员模板?
成员模板是指在类或结构体内部定义的模板成员(如函数或构造函数)。它允许类的成员函数或构造函数支持泛型操作,从而处理不同类型的参数,提升代码的灵活性和复用性。
在 pair
结构体中,成员模板构造函数 template <class U1, class U2> pair(const pair<U1, U2>& p)
就是一个典型的例子。它允许从不同类型的 pair
对象构造当前 pair
对象。
2. 成员模板构造函数的作用
成员模板构造函数的主要作用是支持类型转换。在上面的代码中:
template <class U1, class U2>
pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
- 这个构造函数可以从
pair<U1, U2>
这种pair
对象p1
作为初值来构造pair<T1, T2>
对象。 - 例如,可以从
pair<Derived1, Derived2>
构造pair<Base1, Base2>
,因为Derived1
可以隐式转换为Base1
,Derived2
可以隐式转换为Base2
。
3. 示例中的类型转换
- 基类和派生类关系:
Base1
和Base2
是基类。Derived1
和Derived2
是派生类,分别继承自Base1
和Base2
。
pair
对象构造:- 可以创建一个
pair<Derived1, Derived2>
对象,然后通过成员模板构造函数将其转换为pair<Base1, Base2>
对象。 - 例如:
pair<Derived1, Derived2> p1; pair<Base1, Base2> p2(p1); // 使用成员模板构造函数
- 可以创建一个
4. 成员模板的优势
- 类型无关性:成员模板允许类的成员函数或构造函数处理多种类型,无需为每种类型单独编写代码。
- 扩展性:可以轻松添加更多泛型操作,例如在
pair
结构体中添加其他泛型成员函数。 - 类型转换支持:成员模板构造函数支持类型兼容的隐式转换,特别适用于继承层次结构中的类型转换。
5. 成员模板的限制
- 成员模板构造函数不能替换默认构造函数或拷贝构造函数。
- 类型转换必须合法,例如派生类可以隐式转换为基类,但反之则不行。
成员模板是C++泛型编程的重要工具,它允许类的成员函数或构造函数支持泛型操作,增强了代码的复用性和灵活性。在 pair
结构体中,成员模板构造函数使得类型转换更加方便,特别是在继承层次结构中,派生类对象可以隐式转换为基类对象。这种特性在STL等标准库中广泛应用,是C++高效编程的核心技术之一。
成员模板在智能指针中的应用
template<typename _Tp>
class shared_ptr : public __shared_ptr<_Tp> {
public:
template<typename _Tp1>
explicit shared_ptr(_Tp1* __p) : __shared_ptr<_Tp>(__p) {}
// 其他成员函数和数据成员可能存在但在示例中省略...
};
// 假设这是基类和派生类的定义
class Base1 {
// 基类成员
};
class Derived1 : public Base1 {
// 派生类成员
};
int main() {
Base1* ptr = new Derived1; // up-cast
shared_ptr<Base1> sptr(new Derived1); // 模拟 up-cast
return 0;
}
1. 类模板
template<typename _Tp>
class shared_ptr : public __shared_ptr<_Tp> {
// ...
};
shared_ptr
是一个类模板,模板参数为_Tp
,表示它可以管理任意类型的指针。- 它继承自
__shared_ptr<_Tp>
,后者是实现智能指针功能的基类。
2. 成员模板构造函数
template<typename _Tp1>
explicit shared_ptr(_Tp1* __p) : __shared_ptr<_Tp>(__p) {}
- 这是一个成员模板构造函数,模板参数为
_Tp1
,允许从不同类型的指针构造shared_ptr
对象。 explicit
关键字:禁止隐式类型转换,避免意外的构造行为。- 构造函数体:通过调用基类
__shared_ptr<_Tp>
的构造函数,将_Tp1*
类型的指针__p
转换为_Tp*
类型,并初始化智能指针。
3. 向上转型
new
一个子类,这个指针类型是指向父类,是可以的,叫做向上转型。
在 main
函数中,演示了两种向上转型的方式:
Base1* ptr = new Derived1; // 传统指针的向上转型
shared_ptr<Base1> sptr(new Derived1); // 智能指针的向上转型
- 传统指针向上转型:将派生类
Derived1
的指针赋值给基类Base1
的指针,这是合法的,因为Derived1
继承自Base1
。 - 智能指针向上转型:通过成员模板构造函数,将
Derived1*
类型的指针传递给shared_ptr<Base1>
,实现智能指针的向上转型。这种方式不仅安全,还能自动管理资源。
4. 成员模板的优势
- 类型灵活性:成员模板构造函数允许
shared_ptr
从不同类型的指针构造,增强了代码的通用性。 - 安全性:通过
explicit
关键字和智能指针的资源管理,避免了传统指针的潜在问题(如内存泄漏)。 - 多态支持:结合继承和多态,成员模板使得智能指针可以统一管理基类和派生类对象。
成员模板是C++模板编程中的重要特性,它允许类的成员函数或构造函数支持泛型操作,从而处理不同类型的参数。在 shared_ptr
类模板中,成员模板构造函数使得智能指针可以灵活地从不同类型的指针构造,并支持向上转型,增强了代码的复用性和安全性。这种技术在智能指针和多态编程中广泛应用,是C++高效编程的核心技术之一。
模板特化
#include <iostream>
// 模板声明
template <class Key>
struct hash {};
// char类型的模板特化
template<>
struct hash<char> {
size_t operator()(char x) const { return x; }
};
// int类型的模板特化
template<>
struct hash<int> {
size_t operator()(int x) const { return x; }
};
// long类型的模板特化
template<>
struct hash<long> {
size_t operator()(long x) const { return x; }
};
int main() {
std::cout << hash<long>()(1000); // 输出:1000
return 0;
}
1. 模板声明
template <class Key>
struct hash {};
- 这是一个通用的模板声明,定义了一个名为
hash
的模板结构体。 - 模板参数
Key
是一个占位符类型,表示hash
结构体可以支持任意类型。 - 目前,这个结构体是空的,没有具体的实现。
2. 模板特化
模板特化是指为特定的模板参数提供专门的实现。在代码中,hash
结构体针对 char
、int
和 long
类型进行了特化。
-
char
类型的模板特化:template<> struct hash<char> { size_t operator()(char x) const { return x; } };
template<>
表示这是一个模板特化。hash<char>
为char
类型提供了专门的实现。operator()
是一个函数调用操作符,接受一个char
类型的参数x
,并返回x
的哈希值(这里直接返回x
本身)。
-
int
类型的模板特化:template<> struct hash<int> { size_t operator()(int x) const { return x; } };
- 类似地,
hash<int>
为int
类型提供了专门的实现。 operator()
接受一个int
类型的参数x
,并返回x
的哈希值。
- 类似地,
-
long
类型的模板特化:template<> struct hash<long> { size_t operator()(long x) const { return x; } };
hash<long>
为long
类型提供了专门的实现。operator()
接受一个long
类型的参数x
,并返回x
的哈希值。
3. 模板特化的使用
在 main
函数中,使用了 hash<long>
的特化版本:
std::cout << hash<long>()(1000); // 输出:1000
hash<long>()
创建了一个hash<long>
的临时对象。operator()
被调用,计算并返回1000
的哈希值(这里直接返回1000
本身)。- 结果通过
std::cout
输出。
模板特化的优势
- 类型特定优化:模板特化允许为特定类型提供定制化的实现,从而提高代码的性能和效率。
- 灵活性:在保持模板通用性的同时,可以为某些类型提供特殊处理。
- 代码清晰:通过特化,可以将不同类型的逻辑分离,使代码更易于理解和维护。
模板特化是C++模板编程中的重要特性,它允许为特定类型提供专门的实现。在代码中,hash
结构体针对 char
、int
和 long
类型进行了特化,为每种类型提供了定制化的哈希函数实现。这种技术在处理不同类型的数据时非常有用,能够提高代码的灵活性和性能。
模板偏特化
偏特化,即局部特化。
个数的偏特化
第一种是个数的偏,比如下面的模板有两个,特化其中一个为bool
类型。
#include <iostream>
// 泛型模板
template<typename T, typename Alloc = /* 默认分配器类型,具体省略 */>
class vector {
public:
// 此处可定义泛型vector的通用成员函数和成员变量等
void printInfo() const {
std::cout << "This is a general vector." << std::endl;
}
};
// 偏特化模板,针对T为bool类型的情况
template<typename Alloc = /* 默认分配器类型,具体省略 */>
class vector<bool, Alloc> {
public:
// 此处可定义bool类型vector特有的成员函数和成员变量等
void printInfo() const {
std::cout << "This is a vector of bool type." << std::endl;
}
};
模板泛化与偏特化概述
在C++中,模板是一种强大的编程机制,可以实现代码的泛型(通用)编程。模板分为函数模板和类模板。类模板可以有一个或多个类型参数。模板泛化是一种最通用的模板定义形式,它适用于各种类型的参数。比如上面代码中的template<typename T, typename Alloc>
定义的vector
类模板。
而模板偏特化(partial specialization)则是在泛化模板的基础上,针对某些特定的参数情况进行更具体的实现。它允许在保持一部分模板参数可变的同时,对另外一部分参数做出特定的指定,提供更加定制化的行为。
个数的偏特化要点
- 减少类型参数个数的特化情况:个数的偏特化并不是指减少模板参数个数本身,而是在模板参数整体个数不变的情况下,对某些特定参数类型或数值进行特化。以上述的
vector
类模板为例,泛化模板有两个类型参数T
和Alloc
。而偏特化版本中template<typename Alloc>
class vector<bool, Alloc>
依然有两个参数,只是将第一个参数T
固定为了bool
类型,第二个参数Alloc
依然可以是任意类型(默认为指定的默认值)。这种做法的意义在于,当存储的元素类型为bool
时,可能有特殊的内存优化需求或其他行为,例如在标准库中,vector<bool>
实际并不是简单地按一位一位存储bool
值的,而是采用了一些压缩存储的技巧来节省空间,此时通过偏特化就可以对这一特定情况进行专门的实现和优化。 - 编译器匹配原则:当实例化模板时,编译器会根据传入的模板参数类型,遵循一定的匹配原则在泛化模板和所有的偏特化版本中寻找最合适的模板实例。如果传入的参数能完全匹配某个偏特化模板的特化条件,编译器就会使用该偏特化模板来实例化类;而只有当没有合适的偏特化模板与之匹配时,才会使用泛化模板。例如当我们如下方式实例化类:
vector<int> intVec; // 使用泛化模板,因为T为int不是特化的bool
intVec.printInfo(); // 输出:This is a general vector.
vector<bool> boolVec; // 使用偏特化模板,因为T为bool
boolVec.printInfo(); // 输出:This is a vector of bool type.
模板偏特化中的个数偏相关特性允许程序员在不改变模板参数个数的整体框架下,针对特定类型参数的取值情况提供定制化的实现,使得代码在保持通用性的同时,能够对一些常见的、有特殊需求的类型做出优化和特殊处理,从而提高代码的效率和适应性。
范围的偏特化
第二种是范围的偏,可以把参数的范围缩小,比如下面的代码,如果只要传进来的是指针,就使用下面这种。而指针指向的是什么,不需要考虑。
// 泛型模板
template <typename T>
class C {
};
// 模板偏特化:针对指针类型的偏特化版本
template <typename T>
class C<T*> {
};
// 等价的模板偏特化(使用不同的类型参数名)
template <typename U>
class C<U*> {
};
#include <string>
int main() {
C<std::string> obj1;
C<std::string*> obj2;
return 0;
}
上述代码示例是针对指针类型的模板偏特化。原始泛型模板 template <typename T> class C
可以接受任何类型作为模板参数 T
。然而,通过 template <typename T> class C<T*>
,定义了一个针对指针类型的偏特化版本。
- 示例分析:
- 泛型模板
C<T>
:这是一个通用的模板,适用于任意类型T
。例如,C<std::string>
会实例化这个泛型版本。 - 偏特化模板
C<T*>
:这是一个针对指针类型的偏特化版本。当模板参数是指针类型(如std::string*
)时,编译器会优先选择这个偏特化版本。例如,C<std::string*>
会实例化这个偏特化版本。 - 等价偏特化
C<U*>
:这个版本与C<T*>
功能相同,只是使用了不同的类型参数名U
。这种写法只是为了展示语法上的灵活性,实际功能与C<T*>
完全一致。
- 泛型模板
范围的偏特化
在模板偏特化中,范围的偏特化指的是特化版本对模板参数的部分范围进行限定,而不是完全限定。例如:
- 指针类型的偏特化:
C<T*>
特化了所有指针类型,无论T
是什么类型,只要T*
是指针类型,就会匹配这个偏特化版本。 - 部分参数的特化:偏特化并不需要完全指定所有模板参数。例如,
template <typename T, typename U> class C<T, U*>
只对第二个参数U
进行指针类型的特化,而第一个参数T
仍然保持泛型。
实例化和使用
在 main
函数中,通过 C<std::string> obj1;
和 C<std::string*> obj2;
分别实例化了泛型版本和偏特化版本的 C
类。
C<std::string> obj1;
:使用泛型模板C<T>
,因为std::string
不是指针类型。C<std::string*> obj2;
:使用偏特化模板C<T*>
,因为std::string*
是指针类型。
模板偏特化的用途
模板偏特化的主要用途包括:
- 性能优化:针对特定类型(如指针类型)提供更高效的实现。
- 行为定制:为不同类型(如内置类型、自定义类型)提供不同的行为。
- 适配特殊类型:处理特殊类型特性,例如
const
、volatile
限定符等。
通过模板偏特化,可以在保持模板代码通用性的同时,为特定类型或类型组合提供定制化的实现,从而提高代码的灵活性和效率。
模板模板参数
模板中的一个模板参数也为模板。
// 模板定义,其中T是类型模板参数,Container是模板模板参数
template<typename T, template <typename T> class Container>
class XCls
{
private:
Container<T> c; // 使用模板模板参数实例化一个对象c
public:
// 其他成员函数和数据成员
};
// 定义一个类型别名Lst,它是std::list的模板实例化
template<typename T>
using Lst = std::list<T, std::allocator<T>>;
// 尝试实例化XCls,这里第一个实例化是错误的,第二个是正确的
// XCls<std::string, std::list> mylst1; // 错误,std::list需要第二个模板参数
XCls<std::string, Lst> mylst2; // 正确,使用类型别名Lst,Lst已经包含了完整的模板参数形式
只有模板的尖括号中<>,typename
和class
写哪个都行,互通。
1. 什么是模板模板参数?
模板模板参数(Template Template Parameter)是指模板参数本身也是一个模板。它允许在定义模板时,接受另一个模板作为参数,从而增强模板的灵活性和通用性。
2. 语法结构
模板模板参数的典型语法如下:
template<typename TypeParam, template<typename TypeArg> class TemplateParam>
class OuterTemplate {
TemplateParam<TypeParam> member;
// ...
};
TypeParam
是普通的类型模板参数。TemplateParam
是模板模板参数,它本身是一个模板,接受一个类型参数TypeArg
。
3. 代码中的模板模板参数
在代码中,XCls
模板定义如下:
template<typename T, template <typename T> class Container>
class XCls
{
private:
Container<T> c;
public:
// 其他成员函数和数据成员
};
T
是类型模板参数。Container
是模板模板参数,它必须是一个模板,且接受一个类型参数T
。Container<T> c
使用模板模板参数Container
实例化一个成员变量c
。
4. 模板模板参数的使用
为了正确使用模板模板参数,需要确保传递的模板参数符合要求。代码中定义了一个类型别名 Lst
,第一个参数为string
,第二个模板参数本身为模板,引入Lst
,来作为第二参数。:
template<typename T>
using Lst = std::list<T, std::allocator<T>>;
Lst
是一个模板别名,表示std::list<T, std::allocator<T>>
。Lst
符合XCls
模板模板参数Container
的要求,因为它是一个只接受一个类型参数T
的模板。
5. 实例化示例
-
错误示例:
XCls<std::string, std::list> mylst1; // 错误
- 错误原因:
std::list
需要两个模板参数(元素类型和分配器类型),而XCls
的模板模板参数Container
只接受一个类型参数。
- 错误原因:
-
正确示例:
XCls<std::string, Lst> mylst2; // 正确
- 正确原因:
Lst
是一个符合Container
要求的模板,它只接受一个类型参数。
- 正确原因:
模板模板参数是C++模板编程中的高级特性,它允许模板接受另一个模板作为参数,从而增强代码的灵活性和通用性。在代码中,XCls
模板通过模板模板参数 Container
实现了对任意容器的支持,而类型别名 Lst
则确保了模板模板参数的正确使用。模板模板参数特别适用于容器、算法等需要高度泛化的场景,是C++模板元编程的重要工具。
模板模板参数在智能指针中的应用
template<typename T, template <typename T> class SmartPtr>
class XCls
{
private:
SmartPtr<T> sp;
public:
XCls() : sp(new T) {}
};
XCls<std::string, std::shared_ptr> p1;
XCls<double, std::unique_ptr> p2; // 错误,std::unique_ptr不满足模板模板参数要求
XCls<int, std::weak_ptr> p3; // 错误,std::weak_ptr不满足模板模板参数要求
XCls<long, std::auto_ptr> p4; // std::auto_ptr已废弃不用
在代码中,template<typename T, template <typename T> class SmartPtr>
中的 template <typename T> class SmartPtr
就是一个模板模板参数。它规定了 SmartPtr
是一个模板类型,并且该模板本身需要接受一个类型参数 T
。
1. 模板模板参数的作用
模板模板参数的主要作用是提高代码的复用性和灵活性。通过模板模板参数,可以将模板依赖的另一个模板也作为可配置项,这样在不同场景下,可以传入不同的具体模板来实例化更高层级的模板。
在 XCls
模板中使用 SmartPtr
这一模板模板参数,可以在实例化 XCls
时选择不同类型的智能指针(如 std::shared_ptr
等合适的智能指针类型)来管理对象资源。
2. 模板模板参数的使用限制
模板模板参数所代表的模板必须具有符合要求的模板参数列表。在当前例子里,要求传入的模板类型(SmartPtr
)必须有且仅有一个类型模板参数 T
。
示例中 std::unique_ptr
和 std::weak_ptr
不满足要求,因为它们的实际模板参数列表形式并不是 template <typename T>
这么简单。
std::unique_ptr
的完整模板参数还可能涉及自定义删除器等,std::weak_ptr
涉及到和std::shared_ptr
交互等std::auto_ptr
虽然形式上接近但已经被C++11废弃不用std::shared_ptr
是一个相对符合要求的智能指针类型示例,可以作为模板模板参数的实际传入值
3. 具体使用示例
当实例化 XCls
时,例如 XCls<std::string, std::shared_ptr> p1;
,这里将 std::string
作为 XCls
模板的 T
参数,同时将 std::shared_ptr
这个模板作为 SmartPtr
模板模板参数传入,XCls
类内部的 SmartPtr<T> sp;
将会实例化为 std::shared_ptr<std::string> sp;
,从而可以通过 std::shared_ptr
来管理 std::string
类型的对象资源。
模板模板参数特别适用于容器、智能指针等需要高度泛化的场景,是C++模板元编程的重要工具。