c++函数模板

函数模板时通用的函数描述,他们使用泛型来定义函数,其中的泛型可以用具体的类型代替。将类型作为参数传递给模板,编译器可以生成该类型的函数。

模板建立

//examples

template <typename AnyType>			

void Swap(AnyType& a, AnyType& b)
{
	AnyType temp;
	temp = a;
	a = b;
	b = temp;
}


//调用方式
int main()
{
    int a=32;
    int b=58;
    Swap(a,b);
}

这里的template和typename关键字是必须的,其中typename可以用class代替(前者的兼容性更好),后面的AnyType可以随意替换为其他的合法名称。

工作原理

模板并不创建函数,只能告诉编译器如何定义函数,当编译器知道参数的类型int后会创建一个参数类型为int的函数,将所有的AnyType 替换为int。最终的程序并不会含有模板,而是实际的函数。使用模板可以使多个函数定义更加简单,更加可靠。通常模板会放到头文件中。

重载模板

模板重载和一般的函数模板的操作相同,被重载的模板的函数特征标必须不同。

//examples

template <typename AnyType>
void Swap(AnyType& a, AnyType& b)
{
	
}

template <typename AnyType>
void Swap(AnyType& a, AnyType& b,int c)
{

}

模板的局限

  1. 模板可能无法处理所有的类型,如果模板中含有比较运算符,而参数是数组类型。
  2. 函数中可能出现C++不允许的语法,如结构体相加。

显示具体化

显示具体化就是具体化函数定义,包含完成某些功能的代码。当编译器找到与函数调用匹配的具体化定义是,将使用后该具体化定义,而不再寻找模板。

  • 对于给定的函数名,可以有非模函数、模板函数和显式具体化模板函数以及他们的重载版本。
  • 显示具体化的原型和定义应以template<>大头,并通过名称来中指出类型。
  • 具体化优先于常规模板,而非模板函数优先于具体化和常规模板。
//examples

template<>void Swap<T>(T a,T b);		//声明一个显示具体化的模板,当数据类型T为函数参数时,使用此函数
template<>void Swap(T a,T b);			//因为参数中已经声明是T的显示具体化,所以<T>可以省略,两种方式等价

实例化

  • 隐式实例化:百年一起为使用模板为特定类型生成函数定义,等到一个模板实例。模板并非函数定义,而使用特定的模板实例是函数定义。

  • 显式实例化:直接命令编译器创建特定的实例。使用语法是:声明所需的种类——用<>符号指示类型,并在声明前加上关键字template。

    //examples
    template void Swap<int>(int,int)  //显式实例化声明产生一个int 模板实例
     //在使用函数模板时进行显式具体化
     Swap<int>(a,b);		//参数类型原型不可以为指针或者引用		
    
  • 注意:在同一个文件或者同一个转换单元中使用同一种类型的显式实例化和显示具体化将出错。

具体化

隐式实例化、显式实例化和显示具体化统称为具体化。都白哦是是使用具体类型的哈数定义,而不是通用描述。

编译器对函数版本的选择

编译器对函数版本的选择策略:

  • 创建候选函数列表。其中包含与被调用函数的名称相同的函数和模板函数。
  • 使用候选函数列表创建可行函数列表。可行函数列表中的函数参数数目都正确。其中会对函数参数进行隐式转换。以此达到实参和形参完全匹配。
  • 确定最佳的可行函数,如果过没有就是函数调用出错。确定从最佳到最差的顺序
    1. 完全匹配,常规函数优先于模板。
    2. 提升转换,将char或short转化为int等。
    3. 标准转换,如将int转化为char。
    4. 用户自定义的转换,如类声明中定义的转换。

完全匹配时允许的无关紧要的转换

从实参 到形参
Type Type&
Type& Type
Type[] * Type
Type(argument-list) Type(*)(argument-list)
Type const Type
Type volatile Type
Type * const Type
Type * volatile Type

关键字decltype(c++11)

用来设定一个值的类型,调用格式如下:

decltype(expression)var;
//examples
int a=32;
decltype(a)x;		//x的类型为int

具体核对方案:

  • 如果expression是一个没有用括号括起的标识符,则var的类型与该标识符的类型相同,包括const等限定符。

  • 如果expression是一个函数调用,则var的类型与函数的返回类型相同。

    注意:并不会实际调用函数。编译器通过查看函数的原型来获悉返回类型,而无需实际调用函数。

  • 如果expression是一个左值,则var为指向其类型的引用。这好像意味着前面的w应为引用类型,因为x是一个左值。但别忘了,这种情况已经在第一步处理过了。要进入第三步,expression不能是未用括号括起的标识符。那么,expression是什么时将进入第三步呢?一种显而易见的情况是,expression是用括号括起的标识符。

  • 如果前面的条件都不满足,则var的类型与expression的类型相同

posted @   布拉多1024  阅读(88)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示