c++总结-05-模板与泛型编程
模板与泛型编程常用技巧
- 目录
- 一、基础阶段
- 1.1 函数模板
- 1.2 类模板
- 1.3 模板特化与偏特化
- 1.4模板编译模型
- 1.5 变量模板
- 1.6 别名模板
- 1.7 模板模板参数
- 二、中级阶段
- 2.1 SFINAE
- 2.2 enable_if:SFINAE应用
- 2.3 void_t技术:SFINAE应用
- 2.4 萃取
- 2.5 可变参数模板
- 2.6 完美转发
- 三、高级阶段
- 3.1 C++模板设计模式
- 四、模板实践
- 4.1智能指针实现
目录
一、基础阶段
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;
}
结果: