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

发布于:2025-06-15 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

一、基础阶段

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 container

Container<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 pair

Pair<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 type

Box<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 triple

Triple<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成员时触发SFINAE

struct 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; SFINAE
	system("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的值类别转发给target
    target(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;
}

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


网站公告

今日签到

点亮在社区的每一天
去签到