为什么函数模板没有偏特化?

背景

先来看一个毁三观的demo

//first template
template <typename A, typename B>
void f(A a, B b) {
    std::cout << "Normal version." << std::endl;
}
template<>
void f<int, int>(int a, int b) {
	std::cout << "Full Special version." << std::endl;
}

// second template
template <typename A>
void f(A a, int b) {
    std::cout << "Partial version." << std::endl;
}

int main()
{
	int a = 10;
	double b = 12;	
	f(a, b);
	f(a, a);
}
// result
Normal version.
Partial version.

分析

上面的代码可以看到,函数在调用时候顺序是:

  1. 有匹配的no-template函数就优先调用,没有再进行下一步
  2. 先找合适的模板,在本demo中,合适的模板就是second template
  3. 找到模板后,在模板的实例化函数中找一个最合适的
  4. 如果该模板没有最合适的函数,那么就由编译器根据该模板自己实例化一个
    在这里插入图片描述

结论

从上面的代码可以看到,函数并不需要偏特化,只要重载一个模板就可以实现偏特化的效果。切记,C++函数没有偏特化,下面的写法会报错,简单说不允许特化的时候template后面<>还有值存在
在这里插入图片描述
在这里插入图片描述

思考

目前经常推荐enable_if, 但当我实际使用的时候,会发现编译器提醒模板重定义, 所以模板也是存在重定义问题,这里重定义的原因是,两个模板的签名都是:<typename T1, typename T2, typename dummy> + void show(T1, T2), 所以在下面的使用enable_if方法时,只能出现一个enable_if, 这个函数是为了限制某个参数必须满足某个条件。

template<typename T1, typename T2, typename dummy = enable_if_t<is_same<T2, int>::value>>
void show(T1 a, T2 b)
{
        cout<<" partial T2 = int" << endl; 
}

template<typename T1, typename T2, typename dummy = enable_if_t<is_same<T2, float>::value>>
void show(T1 a, T2 b)
{
        cout<<" partial T2 = float" << endl; 
}

如果想实现上面的效果,可以下面操作, 我们知道,模板参数也是可以穿入指针类型的,这里enable_if一旦成立会生成void*。这里为什么没有模板重定义问题呢?我们知道typename不管传入类型是啥,比如int, float, char 反正模板签名里类型就是typename, 但是指针就不一样了,int*, char* ,double* 在模板签名里看就是不同的类型了,所以不会出现重复定义的问题。这个要好好想明白,不然就弄不懂类偏特化时候enable_if的使用方法。

template<typename T1, typename T2, enable_if_t<is_same<T2, int>::value>*  dummpy = nullptr >
void show(T1 a, T2 b)
{
        cout<<" partial T2 = int" << endl; 
}

template<typename T1, typename T2, enable_if_t<is_same<T2, float>::value>*  dummpy = nullptr >>
void show(T1 a, T2 b)
{
        cout<<" partial T2 = float" << endl; 
}

以上的实验放在类模板上也是成立的

总结

函数只有全特化,也就是说一个函数模板只能全特化,但是可以进行模板的重载,重载的时候注意typename和指针的效果

参考

http://www.gotw.ca/publications/mill17.htm

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译器优化是指编译器对程序进行的对执行速度、内存占用和二进制文件大小等方面的优化操作。它可以通过调整代码结构、减少冗余操作、使用更高效的算法等方式来提高程序的性能。 堆栈和堆内存是计算机内存中两种不同的存储方式。堆栈是由操作系统自动分配和释放的一种内存空间,用于存储函数调用时的局部变量和参数等。堆内存则是由程序员手动分配和释放的一块内存空间,用于存储动态分配的变量或对象。 普通指针是一种基本数据类型,它保存了一个变量或对象的内存地址。智能指针则是一种类,它封装了指针的操作,并提供了自动内存管理的功能,可以在对象不再被使用时自动释放它所指向的内存空间。 引用是一个变量或对象的别名,它与原变量或对象使用同一块内存空间。常引用是指不能通过引用修改其所指向的变量或对象的引用。 类型转换是将一个数据类型的值转换为另一个数据类型的值的过程。常见的类型转换包括隐式类型转换和显式类型转换。 析构函数是一种特殊的成员函数,它在对象被销毁时自动调用,用于释放对象所占用的资源。 构造函数是一种特殊的成员函数,它在对象被创建时自动调用,用于初始化对象的状态。 基类和派生类是面向对象编程中的概念。基类是一个通用的模板类或抽象类,派生类则是基于基类的一个特定实现。派生类可以继承基类的成员变量和函数,也可以重载基类的函数或添加新的成员。 友元函数和友元类是一种特殊的访问权限,它允许函数或类访问另一个类的私有成员。 模板特化是指为了满足特定类型的需求而对模板进行的特殊化处理。 模板特化是指在模板的基础上,对一部分类型进行特殊化处理。 迭代器是一种对象,它可以遍历容器中的元素,并提供对容器中元素的访问和操作。 容器适配器是一种特殊的容器,它可以通过改变底层容器的数据结构和访问方式来提供不同的功能。例如,栈和队列都是通过容器适配器实现的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值