最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

【c++指南】模板VS手写代码:这场效率对决你站哪边?【下】

网站源码admin1浏览0评论

【c++指南】模板VS手写代码:这场效率对决你站哪边?【下】

非类型模版参数

在 模版【上】章节中模版参数当作类型来处理,实际上模版参数还有非类型模版参数

非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常

量来使用

代码语言:javascript代码运行次数:0运行复制
#define N 10

template<class T>
class myvector
{
public:
	//...

private:
	T _arr[N];
	int _capacity;
};


int main()
{
	myvector<int> v1;
	myvector<double> v2;
	return 0;
}

在上面这段程序当中,N的大小是确定的,即如果要插入100个数据时,N是10是不够的,需要手动将N的大小进行修改;当要插入10个数据时,N是100又太多了,导致很多空间被浪费。因此槽点很多,如果使用非类型模版参数可以改善这种问题。

代码语言:javascript代码运行次数:0运行复制
template<class T,size_t N>
class myvector
{
public:
	//...

private:
	T _arr[N];
	int _capacity;
};

此时N的大小我们可以自行决定,此时一个double类型的vector要开100个空间

代码语言:javascript代码运行次数:0运行复制
myvector<double, 100> v;

int类型的vector要开10个空间

代码语言:javascript代码运行次数:0运行复制
myvector<int, 10> v;

实际上,在C++库中的array(数组)也是这样处理的,那它又和int array[N]有什么区别呢?

C语言中的int array对越界问题检查实际是不严格的。

C++中array对越界问题的检查。

需要注意的是

  • 浮点数、类对象以及字符串是不允许作为非类型模板参数的
  • 非类型的模板参数必须在编译期就能确认结果

特化

通常情况下, 使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些

错误的结果 ,需要特殊处理。

代码语言:javascript代码运行次数:0运行复制
template<class T>
bool Less(T left, T right)
{
	return left < right;
}

int main()
{
	cout << Less(1, 2) << endl;

	Date d1(2025, 3, 1);
	Date d2(2025, 3, 2);
	cout << Less(d1, d2) << endl;
	return 0;
}

结合之前Date类实现,如上一段程序中,可以对日期类进行正确比较。但是也有一些情景是需要特殊处理的。

在上面这段程序当中,明显d3>d4的,运行结果为1才对。但运行几次后发现有两个结果,为什么会出现0这个现象呢?

此时我们进行调试观察d3和d4。

可以发现d4的内存地址是大于d3的内存地址,这也解决了我们的疑惑:这里的比较是指针地址的比较,因此需要引入特化或提供函数支持Date比较 。

函数模版特化

函数模板的特化步骤:

1. 必须要先有一个基础的函数模板

2. 关键字template后面接一对空的尖括号<>

3. 函数名后跟一对<>,<>中指定需要特化的类型

4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇

怪的错误。

使用函数模版特化解决Date*按指针比较的方式。

代码语言:javascript代码运行次数:0运行复制
//函数模版特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}

普通函数同样可以解决问题,且这种方式简单明了,难道不是更香吗?

代码语言:javascript代码运行次数:0运行复制
bool Less(Date* left, Date* right)
{
	return *left < *right;
}

我们再来看看传右值的情况(右值指的是匿名对象、常量等)

代码语言:javascript代码运行次数:0运行复制
template<class T>
bool Less(const T& left, const T& right) //传右值-->左值引用右值要加const
{
	return left < right;
}

//特化
template<>
bool Less<Date*>(Date* const & left, Date* const & right) //const修饰本身
{
	return *left < *right;
}

int main()
{
	cout << Less(new Date(2025, 3, 1), new Date(2025, 2, 28)) << endl;
	return 0;
}

再针对右值的情况下,左值引用要引用右值就必须+const修饰,那在特化的时候,也需要进行对应修改。可以看到,模版函数特化在上面这段程序中显得特别坨,再来看看普通函数的实现。

代码语言:javascript代码运行次数:0运行复制
bool Less(Date* const & left, Date* const & right) //const修饰本身
{
	return *left < *right;
}

因此,为了提高代码的可读性,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化。这样看特化好像无用武之地,别着急我们继续往下看。

类模版特化

全特化

全特化即是将模板参数列表中所有的参数都确定化。

代码语言:javascript代码运行次数:0运行复制
template<class T1,class T2>
class Date
{
public:
	Date()
	{
		cout << "Date<T1,T2>" << endl;
	}
};

template<>
class Date<int*, int*>
{
public:
	Date()
	{
		cout << "Date<int*,int*>" << endl;
	}
};

template<>
class Date<int, int>
{
public:
	Date() 
	{
		cout << "Date<int, int>" << endl;
	}
};
偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。

部分模版参数特化

代码语言:javascript代码运行次数:0运行复制
template<class T>
class Date<T, char>
{
public:
	Date()
	{
		cout << "Date<T,char>" << endl;
	}
};

参数更进一步的限制

代码语言:javascript代码运行次数:0运行复制
template<class T1,class T2>
class Date<T1*, T2*>
{
public:
	Date()
	{
		cout << "Date<T1*,T2*>" << endl;
	}
};

template<class T1, class T2>
class Date<T1&, T2*>
{
public:
	Date()
	{
		cout << "Date<T1&,T2*>" << endl;
	}
};

类模版特化的场景

针对日期进行排序

代码语言:javascript代码运行次数:0运行复制
// 对Less类模板按照指针方式特化
template<>
struct Less<Date*>
{
    bool operator()(Date* x, Date* y) const
    {
        return *x < *y;
    }
};

模版分离编译

为什么在 模版【上】章节说到不建议模版声明与定义分离呢?这里做下解释。

分离编译:一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程。

我们知道C/C++程序运行一般要经历以下步骤:

  1. 预处理 --> 展开头文件、宏替换、条件编译、去掉注释等;
  2. 编译 --> 检查语法、生成汇编代码;
  3. 汇编 --> 将汇编代码转成二进制机器码;
  4. 链接 --> 合成可执行程序,链接函数地址等;

运行时链接出现了报错,我们来具体分析下原因在哪。

解决方案

  • 显示实例化(但是不建议如果有多个类型)
代码语言:javascript代码运行次数:0运行复制
	func2(1);
	func2(200.1);
  • 不做声明和定义分离(避开找地址,在声明处直接有地址了)

总结

优点远大于缺点

【优点】 1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生 2. 增强了代码的灵活性 【缺陷】 1. 模板会导致代码膨胀问题,也会导致编译时间变长 2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2025-04-06,如有侵权请联系 cloudcommunity@tencent 删除c++date编译函数效率
发布评论

评论列表(0)

  1. 暂无评论