当前位置: 首页 > news >正文

c++总结-05-模板与泛型编程

目录

一、基础阶段

1.1 函数模板
template<typename T>
T Max(const T& t1, const T& t2)
{return t1 < t2 ? t2 : t1;
}
template<typename T>
void Swap(T& t1, T& t2)
{T temp = std::move(t1);t1 = std::move(t2);t2 = std::move(temp);}
1.2 类模板
template<typename T>
struct Stack
{
private:vector<T> elments;
public:void Push(const T& obj){elments.push_back(obj);}void Pop(){if (elments.size() > 0){elments.pop_back();}else{throw out_of_range("empty stack");}}T GetTop(){if (elments.size() > 0){return elments.back();}else{throw out_of_range("empty stack");}}};
template<typename T,size_t nSize>
class Array
{
private:T data[nSize];
public:T& operator[](int nIndex){return data[nIndex];}
};int main()
{Array<int, 5> a;for (int i = 0;i<5;i++){a[i] = i + 1;cout << a[i] << "  ";}system("pause");return 0;
}

结果:
在这里插入图片描述

1.3 模板特化与偏特化

模板特化(Template Specialization)和偏特化(Partial Specialization)是C++模板编程中的高级特性,允许你为特定类型或类型组合提供特殊的模板实现
模板特化
模板特化是指为模板的特定参数提供完全不同的实现。分为函数模板特化和类模板特化。

函数模板特化

// 通用模板
template <typename T>
void printType() {std::cout << "Generic type\n";
}// 特化版本 - 针对int类型
template <>
void printType<int>() {std::cout << "int type\n";
}// 使用示例
printType<double>(); // 输出: Generic type
printType<int>();    // 输出: int type

类模板特化

// 通用模板
template <typename T>
class Container {
public:void describe() {std::cout << "Generic container\n";}
};// 特化版本 - 针对char类型
template <>
class Container<char> {
public:void describe() {std::cout << "Character container\n";}
};// 使用示例
Container<double> c1;
c1.describe(); // 输出: Generic containerContainer<char> c2;
c2.describe(); // 输出: Character container

模板偏特化
偏特化(也称为部分特化)允许你为模板的部分参数提供特殊实现。注意:偏特化只适用于类模板,不适用于函数模板。

基本偏特化

// 通用模板
template <typename T, typename U>
class Pair {
public:void describe() {std::cout << "Generic pair\n";}
};// 偏特化 - 当两个类型相同时
template <typename T>
class Pair<T, T> {
public:void describe() {std::cout << "Pair of same types\n";}
};// 使用示例
Pair<int, double> p1;
p1.describe(); // 输出: Generic pairPair<float, float> p2;
p2.describe(); // 输出: Pair of same types
// 通用模板
template <typename T>
class Box {
public:void describe() {std::cout << "Box of value type\n";}
};
// 偏特化 - 针对指针类型
template <typename T>
class Box<T*> {
public:void describe() {std::cout << "Box of pointer type\n";}
};// 使用示例
Box<int> b1;
b1.describe(); // 输出: Box of value typeBox<int*> b2;
b2.describe(); // 输出: Box of pointer type

结果:
在这里插入图片描述
多参数偏特化

// 通用模板
template <typename T, typename U, typename V>
class Triple {
public:void describe() {std::cout << "Generic triple\n";}
};// 偏特化 - 当第一个和第三个类型相同时
template <typename T, typename U>
class Triple<T, U, T> {
public:void describe() {std::cout << "Triple with first and last same type\n";}
};// 使用示例
Triple<int, double, char> t1;
t1.describe(); // 输出: Generic tripleTriple<float, int, float> t2;
t2.describe(); // 输出: Triple with first and last same type

在这里插入图片描述
示例:封装一个判断是否为指针的工具

template<typename T>
struct Is_pointer {static constexpr bool value = false;
};template<typename T>
struct Is_pointer<T *> {static constexpr bool value = true;
};int main()
{cout << Is_pointer<int>::value << endl;cout << Is_pointer<int*>::value << endl;system("pause");return 0;
}

结果:
在这里插入图片描述

1.4模板编译模型

模板编译模型是指C++编译器处理模板代码的方式和规则,它决定了模板如何被实例化、链接以及最终生成可执行代码。
包含模型- 最常用
核心思想:模板定义必须在使用它的每个翻译单元中都可见
特点:

  • 模板的声明和定义都放在头文件中
  • 每个使用模板的源文件都会包含完整定义
  • 编译器在每个翻译单元实例化所需特化

示例:

// vector.h
template <typename T>
class Vector {
public:void push_back(const T& value) {// 实现直接写在头文件中}
};

显式实例化模型
核心思想:提前显式声明需要的模板实例化
** mytemplate.h**

#pragma once
// mytemplate.h
template <typename T>
T add(T a, T b);  // 只有声明

mytemplate.cpp

#include "Mytemplate.h"
// mytemplate.cpp
template <typename T>
T add(T a, T b) { return a + b; }// 显式实例化
template int add<int>(int, int);
template double add<double>(double, double);

main.cpp

#include <iostream>
#include "Mytemplate.h"
using namespace std;int main()
{cout<<add(100, 200) << endl;cout << add(12.3, 22.65) << endl;system("pause");return 0;
}

特点:

  • 模板定义可放在.cpp文件
  • 需预先知道所有需要的实例化
  • 减少重编译但灵活性低
1.5 变量模板

变量模板的基本定义形式

template<typename T>
constexpr T pi = T(3.1415926535897932385L);  // 变量模板声明和定义

使用方式:

float f = pi<float>;       // 3.14159265f
double d = pi<double>;     // 3.141592653589793
long double ld = pi<long double>; // 3.1415926535897932385L

基本用途
(1) 类型相关的常量

#include <numeric>template<typename T>
constexpr T max_value = std::numeric_limits<int>::max();int main()
{cout << max_value<int> << endl;cout << max_value<double> << endl;system("pause");return 0;
}

结果:
在这里插入图片描述
(2) 简化类型特征访问

template<typename T>
constexpr bool Is_integral_v = std::is_integral<T>::value;int main()
{static_assert(Is_integral_v<int>);    // 通过static_assert(!Is_integral_v<float>); // 通过system("pause");return 0;
}
1.6 别名模板

别名模板是C++11引入的一项重要特性,它允许我们为模板创建类型别名,极大地简化了复杂类型表达式的书写,提高了代码的可读性和可维护性。
别名模板(Alias Template)是一种可以参数化的typedef,它能够为模板类型创建别名:

基本语法

template<模板参数列表>
using 别名 = 类型表达式;
template<typename T>
using Vec = std::vector<T>;  // Vec<T> 是 std::vector<T> 的别名

基本使用

Vec<int> v1;        // 等价于 std::vector<int>
Vec<std::string> v2; // 等价于 std::vector<std::string>
1.7 模板模板参数

模板模板参数是指接受一个类模板作为参数的模板参数。它允许你在不指定具体类型的情况下,传递整个模板作为参数。

基本语法

template <template <typename> class Container>
class Wrapper {Container<int> data;  // 使用传入的模板实例化
};

使用示例

template <typename T>
class MyVector { /*...*/ };Wrapper<MyVector> w;  // 将MyVector模板作为参数传递

现在有一个需求,创建一个TyClass的类模板,这个类模板,有一个成员变量myc,这个成员变量是一个容器(可能是一个vector或者list等)。现在希望在实例化这个类模板时候能够通过模板参数指定myc是什么类型的容器,以及指定这个容器中所装的元素类型。比如,

TyClass<int,vector> myvectobj;
TyClass<double,list> mylistobj;

实现代码:

template<typename T,template<typename>
class Container = std::vector>class TyClass
{
private:Container<T> myc;
public:void Push(const T& obj){myc.push_back(obj);}void Print(){for (const auto &node :myc){cout << node << endl;}}
};int main()
{TyClass<int> tc;tc.Push(1);tc.Push(2);tc.Push(3);tc.Push(4);tc.Print();system("pause");return 0;
}

结果:
在这里插入图片描述

二、中级阶段

2.1 SFINAE

SFINAE(Substitution Failure Is Not An Error)是C++模板元编程中的核心机制,它允许编译器在模板参数替换失败时优雅地忽略该候选而非报错。

template<class T>
void f(typename T::type  i) {};  // 当T没有::type成员时触发SFINAEstruct X { using type = int; };
struct Y {};int main()
{f<X>(0);  // 匹配f<Y>(0);  // SFINAE忽略,若无其他重载则报未找到匹配函数system("pause");return 0;
}

结果:
在这里插入图片描述

编译器并不认为这个函数模板有错,这就是所谓的“替换失败并不是一个错误”,对于Y类型,并没有type,但是对于其他的类型可能就存在,比如X。但是为什么会报“未找到匹配的重载函数”呢?这是由于函数模板不匹配,编译器又找不到其他适合的f(),所以编译器才报错.

2.2 enable_if:SFINAE应用

标准定义

template<bool B, class T = void>
struct enable_if {};template<class T>
struct enable_if<true, T> { using type = T;  // 只有当B=true时才有type成员
};

辅助类型别名 (C++14起)

 template<bool B, class T = void>
using enable_if_t = typename enable_if<B, T>::type;

示例:

template<bool b,typename T = void>
struct enableIF
{};template<typename T>
struct enableIF<true, T>
{using Type = T;
};template<typename T>
typename enableIF<std::is_integral<T>::value, T>::Type Incred(T a)
{a++;return a;
}int main()
{cout << Incred(12) << endl;;//cout << Incred(12.2) << endl; SFINAEsystem("pause");return 0;
}

结果:
在这里插入图片描述

2.3 void_t技术:SFINAE应用

std::void_t是C++17中引入的,它其实是一个别名模板,源码非常简单,大概如下:

template<tpename...Args>
using void_t = void;

示例:

struct hasType {using intType = int;void fun() {}
};struct noType {void fun() {}
};
//泛化版本
template<typename T,typename U = void_t<>>
struct HasTypeMem :std::false_type
{};//特化版本
template<typename T>
struct HasTypeMem<T, std::void_t<typename T::intType> > :std::true_type
{};int main()
{	cout << HasTypeMem<noType>::value << endl;cout << HasTypeMem<hasType>::value << endl;system("pause");return 0;
}

结果:
在这里插入图片描述

SFINAE机制:

  • 如果 T 没有 intType,typename T::intType会导致替换失败,但这不是错误,编译器会回退到泛化版本(false_type)。
  • 如果 T 有 intType,替换成功,选择特化版本(true_type)。
2.4 萃取

类型萃取是一种编译时类型检查与操作技术,通过模板类和模板特化实现,主要用于:

  • 检查类型特性(如是否为指针、是否为算术类型等)
  • 修改类型特性(如移除const、添加引用等)
  • 根据类型特性实现条件编译

类型分类检查

std::is_void<T>         // 是否为void类型
std::is_integral<T>     // 是否为整型
std::is_floating_point<T> // 是否为浮点型
std::is_array<T>        // 是否为数组

类型修饰检查

std::is_const<T>        // 是否有const限定
std::is_pointer<T>      // 是否为指针
std::is_reference<T>    // 是否为引用

类型关系

std::is_same<T, U>      // 类型是否相同
std::is_base_of<Base, Derived> // 是否为基类
std::is_convertible<From, To> // 是否可隐式转换

示例:

template<typename T>
struct TraitsType;template<>
struct TraitsType<char>
{using RetType = int;
};template<>
struct TraitsType<int>
{using RetType = __int64;
};template<typename T>
auto GetSum(T* begin, T* end)
{using retType = typename TraitsType<T>::RetType;cout << typeid(T).name() << "==>" << typeid(retType).name() << endl;retType sum{};for (;;){sum += (*begin);if (begin == end){break;}begin++;}return sum;}int main()
{int a1[] = { 1,3,5,7 };int a2[] = { 500000000,500000000,700000000 };char a3[] = "abc";cout << GetSum(&a1[0], &a1[3]) << endl;cout << GetSum(&a2[0], &a2[2]) << endl;int nsum = (int)GetSum(&a3[0], &a3[2]);cout << nsum << endl;system("pause");return 0;
}

结果:
在这里插入图片描述

自己实现is_void

template<typename T>
struct Is_void
{static const int value = 0;
};template<>
struct Is_void<void>
{static const int value = 1;
};int main()
{cout << Is_void<int>::value << endl;cout << Is_void<void>::value << endl;system("pause");return 0;
}

结果:
在这里插入图片描述
自己实现is_same

template<typename T1,typename T2>
struct Is_same
{static const int value = 0;
};template<typename T1>
struct Is_same<T1,T1>
{static const int value = 1;
};int main()
{cout << Is_same<int,int>::value << endl;cout << Is_same<void,int>::value << endl;system("pause");return 0;
}

结果:
在这里插入图片描述

2.5 可变参数模板

可变参数模板是C++11引入的一项强大特性,允许模板接受任意数量的模板参数。它是实现泛型编程的重要工具。

可变参数模板使用省略号(…)语法来表示可以接受任意数量的参数:

template<typename... Args>
class MyClass {};
template<typename... Args>
void myFunction(Args... args) {}

其中:
Args 是模板参数包
args 是函数参数包

使用示例
1.递归展开
最常见的展开方式是递归:

void Print()
{cout << endl;
}template<typename T,typename...U>
void Print(T ft, U...args)
{cout << ft << " ";Print(args...);
}int main()
{Print("I","love","China",1314);system("pause");return 0;
}

结果:
在这里插入图片描述
2. 使用折叠表达式 (C++17)
C++17引入了折叠表达式,简化了可变参数模板的使用:

template<typename...U>
auto Sum(U...args)
{return (... + args);
}int main()
{auto nSum = Sum(1, 2, 3, 4, 66.5);cout << nSum << endl;system("pause");return 0;
}

结果:
在这里插入图片描述
3. sizeof… 运算符
可以获取参数包中的参数数量:

template<typename T,typename...U>
auto Sum2(T ft, U...args)
{if constexpr (sizeof...(args) > 0){return ft + Sum2(args...);}else{return ft;}
}

4.应用场景
元组(Tuple)实现:

template<typename... Types>
class Tuple;

完美转发:

template<typename... Args>
void forwarder(Args&&... args) {someFunction(std::forward<Args>(args)...);
}

工厂函数:

template<typename T, typename... Args>
T* create(Args... args) {return new T(args...);
}

打印任意数量参数:

template<typename... Args>
void log(Args... args) { /*...*/ }
2.6 完美转发

完美转发是C++11引入的一项重要特性,它允许函数模板将其参数无损地转发给其他函数,保持原始参数的值类别(左值/右值)。

完美转发依赖于两个关键特性:

  • 右值引用(T&&)
  • 引用折叠规则(Reference Collapsing)

格式如下:

template<typename T>
void wrapper(T&& arg) {// 保持arg的值类别转发给targettarget(std::forward<T>(arg));
}

引用折叠:
类型定义 折叠结果
T& & T&
T& && T&
T&& & T&
T&& && T&&

示例:

template<typename T>
void func(T&& param) {cout << "传入的是右值" << endl;
}template<typename T>
void funcMiddle(T&& param) {func(std::forward<T>(param));
}int main()
{int num = 2021;funcMiddle(num);funcMiddle(2022);system("pause");return 0;
}

结果:
在这里插入图片描述

三、高级阶段

3.1 C++模板设计模式

策略模式

template<typename T, typename AllocationPolicy>
class ManagedContainer {AllocationPolicy allocator;
public:void* allocate(size_t size) {return allocator.allocate(size);}// ...其他成员函数
};// 内存分配策略
struct MallocPolicy {void* allocate(size_t size) { return malloc(size); }
};struct NewPolicy {void* allocate(size_t size) { return new char[size]; }
};int main()
{// 使用ManagedContainer<int, MallocPolicy> mc;int *pArray = (int *)mc.allocate(100);pArray[0] = 1;pArray[1] = 2;pArray[2] = 3;cout << pArray[0] << " " << pArray[1] << " " << pArray[2] << endl;system("pause");return 0;
}

在这里插入图片描述

奇异递归模板模式

静态多态实现

template<typename Derived>
class Base
{
public:void Interface(){Derived* pDerived = static_cast<Derived*>(this);pDerived->impl();}};class Myderived :public Base<Myderived>
{
public:void impl(){cout << "this is Myderived::impl()\n";}
};template<typename T>
void Process(Base<T>* pBase)
{pBase->Interface();
}int main()
{// 使用Myderived d;Process(&d);system("pause");return 0;
}

应用示例:

template<typename T>
class Base
{
public:void process(){GetSub()->process_impl();}
private:T* GetSub(){T* pSub = static_cast<T*>(this);return pSub;}};class Sub1 :public Base<Sub1>
{
public:void process_impl(){cout << "Sub1::process_impl()\n";}~Sub1(){cout << "~Sub1()\n";}};class Sub2 :public Base<Sub2>
{
public:void process_impl(){cout << "Sub2::process_impl()\n";}~Sub2(){cout << "~Sub2()\n";}
};template<typename T>
void Process(Base<T>* p)
{p->process();
}int main()
{Sub1* ps1 = new Sub1;Sub2* ps2 = new Sub2;Process(ps1);Process(ps2);delete ps1;delete ps2;system("pause");return 0;
}

结果:
在这里插入图片描述

四、模板实践

4.1智能指针实现
template<typename T>
class SharedPtr
{
private:T* m_ptr;int* m_pRefCount;mutex* m_mtx;void Release(){(*m_pRefCount)--;if (*m_pRefCount == 0){delete m_ptr;m_ptr = nullptr;delete m_pRefCount;m_pRefCount = nullptr;}}
public:SharedPtr(T* ptr){m_ptr = ptr;m_pRefCount = new int(1);m_mtx = new mutex;}~SharedPtr(){Release();}SharedPtr(const SharedPtr<T>& obj){m_ptr = obj.m_ptr;m_pRefCount = obj.m_pRefCount;m_mtx = obj.m_mtx;m_mtx->lock();(*m_pRefCount)++;m_mtx->unlock();}SharedPtr<T>& operator=(const SharedPtr<T>& obj){if (this != &obj){Release();m_ptr = obj.m_ptr;m_pRefCount = obj.m_pRefCount;m_mtx = obj.m_mtx;m_mtx->lock();(*m_pRefCount)++;m_mtx->unlock();}return *this;}T* operator->(){return m_ptr;}T& operator*(){return *m_ptr;}int UseCount(){return m_ptr ? *m_pRefCount : 0;}};class Ty
{
public:Ty(int a,int b):m_a(a),m_b(b) { cout << "Ty()\n"; }~Ty() { cout << "~Ty()\n"; }void Print(){cout << "m_a:" << m_a << "   m_b:" << m_b << endl;}
private:int m_a;int m_b;
};int main()
{// 使用{SharedPtr<Ty> sp(new Ty(10, 20));sp->Print();cout << "cout:" << sp.UseCount() << endl;SharedPtr<Ty>sp2(sp);cout << "cout:" << sp.UseCount() << endl;SharedPtr<Ty> sp3 = sp2;cout << "cout:" << sp.UseCount() << endl;sp3->Print();SharedPtr<Ty> sp4(new Ty(1,2));sp4 = sp3;cout << "cout:" << sp.UseCount() << endl;}system("pause");return 0;
}

结果:
在这里插入图片描述

相关文章:

  • 大IPD之——学习华为市场洞察,为战略找到方向(四)
  • 层压板选择、信号完整性和其他权衡
  • 日常开发工作流程梳理
  • 16 celery集成其他工具
  • 【论文解读】AgentThink:让VLM在自动驾驶中学会思考与使用工具
  • 【Python 爬虫 防盗链】
  • Java大模型开发入门 (12/15):Agent实战 - 打造能调用外部API的智能助手
  • STM32F4通用定时器TIM9-TIM14讲解及PWM呼吸灯实例解读
  • LeetCode - LCR 173. 点名
  • Magentic-ui项目相关整理
  • 如何自动化测试 DependencyMatcher 规则效果(CI/CD 集成最佳实践)
  • 60天python训练计划----day52
  • Flutter 状态管理与 API 调用的完美结合:从理论到实践
  • RapidNJ软件的安装
  • 独立看门狗(IWDG)与窗口看门狗(WWDG)
  • 6.14星期六休息一天
  • 从0开始学习语言模型--Day01--亲自构筑语言模型的重要性
  • IPv4详解
  • Qt:Qt桌面程序正常退出注意事项
  • 陈小群飞机随笔总结
  • 经销商城建站/免费b站推广网站在线
  • wordpress 很好的博客/武汉服装seo整站优化方案
  • 做推广哪个网站最好/百度网首页官网登录
  • 做架构图的网站/大庆网络推广
  • iis限制网站空间大小/aso优化什么意思
  • 网站开发如何压缩图片/丁的老头seo博客