C++学习笔记——函数模板

函数模板

函数模板的定义和声明

1. 定义:

template <typename T>
void fun(T input)
{
    ...
}
//或
template <class T>
void fun(T input)
{
    ...
}

2. 声明

template <typename T>
void fun(T input);

3. 要点

  • 函数模板不是函数,不能直接调用,在编译期编译期对其进行实例化产生相应的函数才能调用
  • 两对形参实参,fun(3) int:模板实参,3:函数实参,T:模板形参,input:函数形参
  • 编译器对函数模板的两阶段处理:1. 模板语法检查(简略的检查)
    2. 模板实例化后检查(再次检查)
  • 由于需要在编译期进行模板实例化,故C++里规定模板函数为翻译单元级一处定义(每个源文件中可包含相同的函数模板定义)。注意:将函数模板设置为翻译单元级一处定义是为了编译期进行实例化操作,这点与内联函数不同

补充:

内联函数,在头文件中定义函数用内联函数,类内定义的函数会自动引入内联,在调用(准确 的说应该是使用)内联函数时,会直接将函数展开到调用的地方,减小调用成本,但函数较长时不适用。

函数调用时需要保存当前状态,复制实参,返回时恢复原状态,相对来讲成本较高,引入内联函数降低调用成本,由于需要将函数展开,故内联函数为翻译单元级一处定义。

函数模板的特性

1. 函数模板可重载

template <typename T1,typename T2>
void fun(T1 input1,T2 input2)
{
    ...
}

template <typename T>
void fun(T input)
{
    ...
}

2. 模板实参的类型推导

函数模板在实例化时没有显示提供模板实参,这时系统会尝试对模板实参进行推导,推导的原则与auto相似

  1. 函数形参是左值引用或指针

忽略表达式类型中的引用,将表达式类型与函数形参进行模式匹配推导出模板实参。

template <typename T>
void fun(T& input)
{
    ...
}
int main()
{
    int x=2;
    int& y=x;
    fun(y);   //int&-->int  T& =int   -->  T=int
    fun(x);   //int去引用还是int  T&=int  -->  T=int
}
  1. 函数形参是万能引用
template <typename T>
void fun(T&& input)
{
   ... 
}
int main()
{
    int x=3;
    fun(x);  //int 去引用还是 int  T&&=int ---> T=int&(此时触发引用重叠int&&&=int&)
    fun(4);  //T&&=3  --> T=int
}
  1. 函数形参不包含引用

(1)忽略类型表达式的引用 (2)忽略顶层const (3)数组或函数转为相应指针

template <typename T>
void fun(T input)
{
    ...
}

const int& x-->int 
const int* const ptr-->const int*
int x[3] -->int*

注意:

  1. 模板实参不一定能够推导得到
template <typename T1,typename T2>
void fun(T1 in1,T2 in2)
{
    
}
fun(3,5.0);  //推导有歧义
template <typename T1,typename T2>
in1 fun(T2 in2)
{
    
}
fun(3);  //无法推导
//此时可增加缺省模板形参来解决
template <typename T1=double,typename T2>
in1 fun(T2 in2)
{
    
}
fun(3);  //解决问题
  1. 显式指定的模板实参必须从最左开始,依次指定
template <typename T1=double,typename T2>
in1 fun(T2 in2)
{
    
}
fun<int>(3)  //T1=int
//可以显式指定部分模板实参,隐式推导其他模板实参,但得满足显示指定实参从最左开始,不可跳跃来显式指定
//因此尽量将需要显式指定的模板形参,在定义时放在左边
  1. 匹配失败不是错误(SFINAE)

匹配失败并不是模板的错误,而是找不到合适模板。

模板与非模板同时匹配,且匹配等级相同,优先选择非模板版本(特殊版本)

template <typename T1,typename T2>
void fun(T1 in1,T2 in2)
{
    
}

template <typename T1>
void fun(T1 in1,float in2)
{
    
}

fun(2,3.5f);  //此时系统会匹配void fun(T1 in1,float in2)

3. 模板实例化控制

例如fun(x),显式实例化时,包含实例化和调用两个过程,希望能够提前实例化,之后直接调用,提高性能。

template <typename T>
void fun(T x)
{
    
}
template
void fun<int>(int x);  //定义,编译器会实例化函数模板
//但此时相当于引入了一个函数的定义,因此需要满足程序级一处定义原则,不可多处定义。
//若定义不在该翻译单元内部,则需要引入声明
extern template
void fun(int x);

4. 函数模板的特化

对特殊的模板引入特殊的算法,注意是对特例模板引入特殊算法,与函数重载不同。

template <typename T>
void fun(T input)
{
    
}

template <>
void fun<int>(int input)
{
    
}

注意:

  • 函数模板不支持部分特化 ,特化版本的函数一定是以template <>开头。
  • 函数特化比较复杂,稍微写错一点就会出现错误,因此尽量用函数重载来替代函数模板特化
template <typename T>
void fun(T input)
{
    
}

void fun(int input)
{
    
}

5. C++20使用auto代替函数模板

void fun(auto x)
{
    
}
int main()
{
    int x=3;
    fun(3);  //会被实例化为void fun<int>(int x)
}

不能完全替代模板的作用,简单函数可以用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值