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

C++ 模板与泛型编程

1.泛型编程 简介

1.1 定义

C++ 中的泛型编程是一个强大的技术,可以用来编写高效、可扩展且通用的代码。它的核心机制是模板(Templates),泛型编程是一种使用模板来编写代码的方法,可以让程序员编写与具体数据类型无关的代码,通过在编译时生成代码实例来实现类型无关的设计;

1.2 诞生

泛型编程最初诞生于C++中,由Alexander Stepanov[2]和David Musser[3]创立。目的是为了实现C++的STL(标准模板库)。其语言支持机制就是模板(Templates)。模板的精神其实很简单:参数化类型。

把一个原本特定于某个类型的算法或类当中的类型信息抽掉,抽出来做成模板参数T。比如qsort泛化之后就变成了:
 

template<class RandomAccessIterator, class Compare>
void sort(RandomAccessIterator first, RandomAccessIterator last,
Compare comp);

1.3泛型编程样例

通用的加法函数:

// 参数 int
int Add(int& x, int& y) { return x + y; }

/ 参数 double
double Add(double& x, double& y) { return x + y; }

/// 参数 自定义类型
AType Add(AType& x, AType& y) { return x + y; }

使用函数重载虽然可以实现,但是有以下几个不好的地方

      A. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数。

      B. 代码的可维护性比较低,一个出错可能所有的重载均出错。

这个时候,我们可以使用模板函数, 编译器根据不同的类型,利用该模子来自己生成代码;

template <class T>
     T add(T num1, T num2)
{
    return num1+num2;
}

这种编程思想就是 泛型编程 :一种编程范式,编写与类型无关的通用代码,是代码复用的一种手段;模板是泛型编程的基础,包含函数模板和类模板;

2、模板(Template)

模板是 C++ 泛型编程的基础,分为函数模板类模板

2.1 函数模板(function template)

函数模板允许编写与类型无关的函数。通过定义模板参数,函数可以应用于多种类型的数据

template <typename T>
T add(T a, T b) {
    return a + b;
}

 

模板定义以关键字template开始,后接尖括号括住的模板形参表。

     模板形参可以是表示类型的类型形参(type parameter),也可以是表示常量表达式的非类型形参(nontype parameter)。上面程序中的T是类型形参。

int main() {
    std::cout << add(1, 2) << std::endl;       // 输出 3
    std::cout << add(1.1, 2.2) << std::endl;   // 输出 3.3
}

使用函数模板时,编译器会将模板实参绑定到模板形参。编译器将确定用什么类型代替每个类型形参,用什么值代替每个非类型形参,然后产生并编译(称为实例化)该版本的函数。

上面的例子中,编译器用int代替T创建第一个版本,用double代替T创建第二个版本。

函数模板也可以声明为inline

// inlinespecifier follows template parameter list
 
template<typename T> inline T add(const T&, const T&);

2.2 类模板 (class template)

类模板用于创建具有类型参数的类,提供更灵活的数据结构和算法实现;

在定义的类模板中,使用模板形参作为类型或值的占位符,在使用类时再提供具体的类型或值。

template <typename T>
class Stack {
private:
    std::vector<T> elements;

public:
    void push(const T& element) {
        elements.push_back(element);
    }

    T pop() {
        T top = elements.back();
        elements.pop_back();
        return top;
    }
};


与调用函数模板不同,使用类模板时,必须为模板形参显示指定实参。

int main() {
    Stack<int> intStack;
    intStack.push(10);
    intStack.push(20);
    std::cout << intStack.pop() << std::endl;  // 输出 20

    Stack<std::string> stringStack;
    stringStack.push("Hello");
    stringStack.push("World");
    std::cout << stringStack.pop() << std::endl;  // 输出 "World"
}

2. 3 模板特化

模板特化允许为特定的模板参数类型定义特殊行为

2.3.1 完全特化

完全特化为特定类型提供定制实现。

template <>
class Stack<bool> {
private:
    std::vector<uint8_t> elements;

public:
    void push(bool value) {
        elements.push_back(value ? 1 : 0);
    }

    bool pop() {
        uint8_t value = elements.back();
        elements.pop_back();
        return value;
    }
};
2.3.2  偏特化

偏特化允许部分模板参数被固定,剩余的参数仍然可以是泛型的

template <typename T1, typename T2>
class Pair;

template <typename T>
class Pair<T, int> {
    // T 可以是任意类型,但第二个参数固定为 int
};

3. 模板元编程(Template Metaprogramming)

3.1递归模板

用递归模板计算阶乘:

template <int N>
struct Factorial {
    static constexpr int value = N * Factorial<N - 1>::value;
};

template <>
struct Factorial<0> {
    static constexpr int value = 1;
};

int main() {
    std::cout << Factorial<5>::value << std::endl;  // 输出 120
}
3.2 类型特性与 SFINAE

SFINAE(Substitution Failure Is Not An Error)是模板编程中重要的概念,用于启用/禁用模板的特定实现

template <typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
square(T value) {
    return value * value;
}

int main() {
    std::cout << square(5) << std::endl;     // 输出 25
    // std::cout << square(5.5) << std::endl;  // 编译错误
}

4. Concepts(C++20 新特性)

Concepts 提供了一种显式约束模板参数的方法,增强代码的可读性和错误提示

#include <concepts>

template <std::integral T>
T add(T a, T b) {
    return a + b;
}

int main() {
    std::cout << add(1, 2) << std::endl;  // 输出 3
    // std::cout << add(1.1, 2.2) << std::endl;  // 编译错误
}

5.实践建议

  • 选择合适的模板类型
    • 使用 typenameclass 定义模板参数没有功能上的差异,选用风格一致的关键词。
  • 合理使用特化与 SFINAE
    • 不要过度复杂化模板逻辑,避免代码难以维护。
  • 结合 Concepts
    • 对模板参数设置合理的约束,提高错误信息的可读性。
  • 优化编译性能
    • 泛型编程会生成大量代码实例,注意模块化设计,减少重复实例化。

6.小结

C++ 泛型编程提供了极大的灵活性和功能,但也对代码设计能力和理解能力提出了更高的要求。结合实际需求和新的语言特性,可以写出高效、优雅的泛型代码。


http://www.kler.cn/a/413909.html

相关文章:

  • MySQL8 CTE解决不定层级树形迭代问题
  • vue实现滚动条滑动到底部分页调取后端接口加载数据
  • Java全栈开发:宠物医院管理系统项目实战
  • C++网络编程:select IO多路复用及TCP服务器开发
  • Unity版本使用情况统计(更新至2024年11月)
  • 互联网视频推拉流EasyDSS视频直播点播平台视频转码有哪些技术特点和应用?
  • OceanBase 大数据量导入(obloader)
  • 【论文复现】农作物病害叶子图像分割
  • c# httppost请求接口传参数及图片上传,非常实用
  • 电子应用设计方案-28:智能云饭锅系统方案设计
  • 网络--传输层协议--UDP
  • 【C#】调用外部应用
  • Network Link Conditioner Mac 上模拟网络环境工具的安装和使用
  • Mybatis-plus 3.5.9 版本 PaginationInnerInterceptor 插件更新
  • 数据结构与算法学习笔记----并查集
  • 人工智能中的数据结构:构建智能算法的基石
  • 3D技术如何应用到汽车营销中?
  • OpenCV相机标定与3D重建(6)将3D物体点投影到2D图像平面上函数projectPoints()的使用
  • log4c库使用
  • 【数据结构与算法】链表之美-复杂链表的复制与链表的插入排序
  • 占用磁盘100%?Apache DolphinScheduler 日志如何定时清理!
  • IntelliJ+SpringBoot项目实战(十七)--在SpringBoot中整合SpringSecurity和JWT(下B)
  • Excel按固定行数拆分为多个Excel
  • 屏幕触控支持指纹修改
  • 数据集成工具Kafka、Nifi和Talend功能大对比!
  • Jenkins的使用