前言:在C++编程的广阔天地中,模板和仿函数是两大不可或缺的工具。模板以其强大的类型抽象能力,使得代码复用和泛型编程成为可能;而仿函数,则以其函数对象的特性,为算法和容器提供了灵活多变的操作方式。然而,这两者的深入理解和应用,往往需要程序员具备扎实的编程基础和丰富的实践经验
本文我将带领大家走进模板编程的进阶世界,探索仿函数在实际开发中的应用。我们将从模板的基本概念出发,逐步深入到模板的元编程、特化、偏特化等高级话题,同时结合仿函数的定义、使用场景 一起分析
让我们一同踏上这场关于模板进阶与仿函数应用的探索之旅吧!
概念: 仿函数(functor)是一个编程术语,其核心概念是指通过实现一个特定的类,使得这个类的使用看上去像一个函数。
具体来说,仿函数是一个类或结构体,它重载了operator()运算符,从而使得这 个类的对象可以像函数一样被调用。
在我们当前学习的所有知识中,仿函数的用途貌似只涉及到了在STL中的使用,比如我们刚刚了解过的std::priority_queue
,还是之前了解过的std::sort
可以通过传递仿函数作为参数来指定自定义的比较、排序、映射
等操作。这使得STL算法更加灵活和可重用。
优先级队列中的这个排序的操作其实就是通过仿函数比较出来的
仿函数在priority_queue模拟实现中的使用代码示例 (建议结合上一期内容阅读)
// 仿函数的定义
template<class T>
class Less
{
public:
// 重载operator()
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
class Greater
{
public:
// 重载operator()
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
// priority_queue的模板参数 -> Compare就代表调用的仿函数
template<class T, class Container = vector<T>, class Compare = Less<T>>
注意:在库里面less表示升序,greater则表示降序,默认情况下使用的时less升序
Sort算法中仿函数的使用代码示例
int main()
{
vector<int> v = { 7,4,1,2,8,9,4,5 };
sort(v.begin(), v.end());
cout << "less: ";
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
cout << "greater: ";
sort(v.begin(), v.end(),greater<int>());
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
return 0;
}
仿函数的单独使用你可以就把他想象成一个函数
template<class T>
class Less
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
int main()
{
Less<int> L;
cout << L(1, 6) << endl;;
return 0;
}
总结来说,仿函数是一种强大的编程工具,它允许开发者将功能封装在类中,并通过重载operator()运算符来使这些类的对象具有类似函数的行为。这种灵活性使得仿函数在代码复用、状态保存以及STL算法中使用等方面具有广泛的应用价值
模板参数分类类型形参与非类型形参。
我们在之前学的所有模板用的基本上都是类型形参,也就是你传什么,他就用什么,而非类型形参则是相当于固定了一个模板参数的类型
类型形参
template<class T, class T>
// ... 其他待添加内容 ...
非类型形参
template<typename T, size_t N = 10>
class Array {
public:
T data[N];
// ... 其他成员函数 ...
};
int main() {
Array<int, 10> arr; // 创建一个大小为10的整数数组
// ... 使用arr ...
return 0;
}
注意:
浮点数、类对象以及字符串
是不允许作为非类型模板参数的概念: 模板的特化(Template Specialization):在C++中是一种技术,它允许我们为模板的特定类型或值提供定制化的实现。这种技术对于满足特定需求或提高性能非常有用
模板特化主要可以分为两种类型:类模板特化 ,函数模板特化
函数模板的特化步骤:
// 基础的函数模板 -> 函数模板,参数匹配
template<class T>
bool Less(const T& x, const T& y)
{
return x < y;
}
// 函数模板特化 -> 对Less函数模板进行特化
template<>
bool Less<double>(const double& x, const double& y)
{
return x < y;
}
int main()
{
cout << Less(1.1, 6.6) << endl; // 调用特化之后的版本,而不走模板生成了
cout << Less(1, 6) << endl;
return 0;
}
注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给 出,参数类型复杂的函数模板不建议特化!
类模板特化(Class Template Specialization)是C++模板编程中的一种机制,它允许我们为类模板的特定类型或类型组合提供专门的定义。在默认情况下,类模板会为所有类型提供通用的实现,但有时候,我们可能希望对某些特定的类型提供不同的实现。这时,就可以使用类模板特化来实现
全特化是针对类模板的所有模板参数提供专门的定义。全特化的语法与类模板的定义类似,但是需要在尖括号中指定具体的类型
类模板全特化代码示例
template<class T>
class pxt
{
public:
void print()
{
cout << "print()" << endl;
}
};
// 全特化版本,针对int类型
template<>
class pxt <int>
{
public:
void print()
{
cout << "print<int>()" << endl;
}
};
int main() {
pxt<int> P;
P.print(); // 输出 "print<int>()"
pxt<double> T;
T.print(); // 输出 "print()"
return 0;
}
偏特化允许我们对类模板的部分模板参数提供专门的定义。这意味着我们可以为模板参数列表中的一部分参数指定具体的类型,而让其他参数保持通用
template<class T1, class T2>
class pxt
{
public:
void print()
{
cout << "print<T1, T2>()" << endl;
}
};
// 偏特化版本,针对T2为int的情况
template<class T1>
class pxt <T1, int>
{
public:
void print()
{
cout << "print<T1, int>()" << endl;
}
};
int main() {
pxt<double, int> P;
P.print(); // 输出 "print<T1, int>()"
pxt<double, double> T;
T.print(); // 输出 "print<T1, T2>()"
return 0;
}
偏特化能将参数类型特化成不同的类型
// 两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
// 两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
注意:
概念: 一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式
// a.h
template<class T>
T Add(const T& left, const T& right); // 声明
// a.cpp
// 定义
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
// 显示实例化(不推荐)
//template
//int Add(const int& left, const int& right);
// main.cpp
#include"a.h"
int main()
{
Add(1, 2);
Add(1.0, 2.0);
return 0;
}
当程序在编译链接时,编译器找到函数模板地址的,这两个函数当时并没有实例化,所以会导致链接时报错
如果遇到模板分离编译相关的问题,常见的解决方法有两种:
当我们在编程的旅途中深入探索C++的模板和仿函数(Function Objects,也称为函数对象或仿函数对象)时,我们不禁被它们强大的灵活性和表达能力所震撼。模板和仿函数是C++标准库和许多现代编程范式中不可或缺的一部分,它们为我们提供了编写可重用、类型安全且易于维护的代码的强大工具
最后我鼓励大家保持对模板和仿函数的学习热情,不断探索和实践它们的强大功能。通过不断的学习和实践,我们不仅能够提升自己的编程技能,还能够为C++社区的发展贡献自己的力量。让我们一起在模板和仿函数的道路上不断前行,探索编程的无限可能! 谢谢大家支持本篇到这里就结束了,祝大家天天开心!