【C++】面向对象高级开发 | 深入C++模板编程:从基础到高级应用

类模板

  设计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 是一个占位符类型,可以在实例化时替换为具体类型(如 intdouble 等)。模板类支持泛型编程,提高代码的复用性。
  • class complex:定义了一个名为 complex 的类,用于表示复数。

2. 构造函数

complex(T r = 0, T i = 0)
    : re(r), im(i)
{ }
  • 这是 complex 类的构造函数,用于初始化复数的实部 re 和虚部 im
  • 参数 ri 有默认值 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;
  • reim 分别存储复数的实部和虚部。
  • 声明为 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> 定义了一个类型参数 Tclass 可以用 typename 代替,二者在这里等价。
  • inline const T& min(const T& a, const T& b)
    • 定义了一个模板函数 min,接受两个 const T& 类型的参数 ab,并返回较小的那个引用。
    • inline 关键字建议编译器将函数调用替换为函数体的代码,通常用于小型频繁调用的函数,以减少函数调用开销。
  • 函数体return b < a? b : a;
    • 使用条件运算符 ? : 比较 ab,并返回较小的值。
    • 为了使这个操作正确执行,类型 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);,编译器会根据传递的实际参数类型(假设 r1r2stone 类型对象)自动推导出模板参数 T 的具体类型。
  • 因为传递的是 stone 对象,编译器会推导出 Tstone,并调用 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 可以隐式转换为 Base1Derived2 可以隐式转换为 Base2

3. 示例中的类型转换

  • 基类和派生类关系
    • Base1Base2 是基类。
    • Derived1Derived2 是派生类,分别继承自 Base1Base2
  • 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 结构体针对 charintlong 类型进行了特化。

  • 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 输出。

模板特化的优势

  1. 类型特定优化:模板特化允许为特定类型提供定制化的实现,从而提高代码的性能和效率。
  2. 灵活性:在保持模板通用性的同时,可以为某些类型提供特殊处理。
  3. 代码清晰:通过特化,可以将不同类型的逻辑分离,使代码更易于理解和维护。

  模板特化是C++模板编程中的重要特性,它允许为特定类型提供专门的实现。在代码中,hash 结构体针对 charintlong 类型进行了特化,为每种类型提供了定制化的哈希函数实现。这种技术在处理不同类型的数据时非常有用,能够提高代码的灵活性和性能。

模板偏特化

  偏特化,即局部特化。

个数的偏特化

  第一种是个数的偏,比如下面的模板有两个,特化其中一个为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类模板为例,泛化模板有两个类型参数TAlloc。而偏特化版本中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* 是指针类型。
模板偏特化的用途

模板偏特化的主要用途包括:

  1. 性能优化:针对特定类型(如指针类型)提供更高效的实现。
  2. 行为定制:为不同类型(如内置类型、自定义类型)提供不同的行为。
  3. 适配特殊类型:处理特殊类型特性,例如 constvolatile 限定符等。

  通过模板偏特化,可以在保持模板代码通用性的同时,为特定类型或类型组合提供定制化的实现,从而提高代码的灵活性和效率。

模板模板参数

  模板中的一个模板参数也为模板。

// 模板定义,其中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已经包含了完整的模板参数形式

只有模板的尖括号中<>,typenameclass写哪个都行,互通。


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_ptrstd::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++模板元编程的重要工具。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清流君

感恩有您,共创未来,愿美好常伴

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值