Template 基础篇-函数模板

Template所代表的泛型编程是C++语言中的重要的组成部分,我将通过几篇blog对这半年以来的学习做一个系统的总结,本文是基础篇的第一部分。

为什么要有泛型编程

C++是一门强类型语言,所以无法做到像动态语言(python javascript)那样子,编写一段通用的逻辑,可以把任意类型的变量传进去处理。泛型编程弥补了这个缺点,通过把通用逻辑设计为模板,摆脱了类型的限制,提供了继承机制以外的另一种抽象机制,极大地提升了代码的可重用性。

注意:模板定义本身不参与编译,而是编译器根据模板的用户使用模板时提供的类型参数生成代码,再进行编译,这一过程被称为模板实例化。用户提供不同的类型参数,就会实例化出不同的代码。

函数模板定义

把处理不同类型的公共逻辑抽象成函数,就得到了函数模板。

函数模板可以声明为inline或者constexpr的,将它们放在template之后,返回值之前即可。

普通函数模板

下面定义了一个名叫compare的函数模板,支持多种类型的通用比较逻辑。

template<typename T>
int compare(const T& left, const T& right) {
if (left < right) {
return -1;
}
if (right < left) {
return 1;
}
return 0;
} compare<int>(1, 2); //使用模板函数

成员函数模板

不仅普通函数可以定义为模板,类的成员函数也可以定义为模板。

class Printer {
public:
template<typename T>
void print(const T& t) {
cout << t <<endl;
}
}; Printer p;
p.print<const char*>("abc"); //打印abc

为什么成员函数模板不能是虚函数(virtual)?

这是因为c++ compiler在parse一个类的时候就要确定vtable的大小,如果允许一个虚函数是模板函数,那么compiler就需要在parse这个类之前扫描所有的代码,找出这个模板成员函数的调用(实例化),然后才能确定vtable的大小,而显然这是不可行的,除非改变当前compiler的工作机制。

实参推断

为了方便使用,除了直接为函数模板指定类型参数之外,我们还可以让编译器从传递给函数的实参推断类型参数,这一功能被称为模板实参推断。

如何使用

compare(1, 2); //推断T的类型为int
compare(1.0, 2.0); //推断T的类型为double
p.print("abc"); //推断T的类型为const char*

有意思的是,还可以通过把函数模板赋值给一个指定类型的函数指针,让编译器根据这个指针的类型,对模板实参进行推断。

int (*pf) (const int&, const int&) = compare; //推断T的类型为int

当返回值类型也是参数时

当一个模板函数的返回值类型需要用另外一个模板参数表示时,你无法利用实参推断获取全部的类型参数,这时有两种解决办法:

  • 返回值类型与参数类型完全无关,那么就需要显示的指定返回值类型,其他的类型交给实参推断。

    注意:此行为与函数的默认实参相同,我们必须从左向右逐一指定。

    template<typename T1, typename T2, typename T3>
    T1 sum(T2 v2, T3 v3) {
    return static_cast<T1>(v2 + v3);
    } auto ret = sum<long>(1L, 23); //指定T1, T2和T3交由编译器来推断 template<typename T1, typename T2, typename T3>
    T3 sum_alternative(T1 v1, T2 v2) {
    return static_cast<T1>(v1 + v2);
    }
    auto ret = sum_alternative<long>(1L, 23); //error,只能从左向右逐一指定
    auto ret = sum_alternative<long,int,long>(1L,23); //ok, 谁叫你把最后一个T3作为返回类型的呢?
  • 返回值类型可以从参数类型中获得,那么把函数写成尾置返回类型的形式,就可以愉快的使用实参推断了。

    template<typename It>
    auto sum(It beg, It end) -> decltype(*beg) {
    decltype(*beg) ret = *beg;
    for (It it = beg+1; it != end; it++) {
    ret = ret + *it;
    }
    return ret;
    } vector<int> v = {1, 2, 3, 4};
    auto s = sum(v.begin(), v.end()); //s = 10

实参推断时的自动类型转换

编译器进行模板实参推断时通常不会对实参进行类型转换,只有以下几种情况例外:

  • 普通对象赋值给const引用 int a = 0; -> const T&
  • 数组名转换为头指针 int a[10] = {0}; -> T*
  • 函数名转换为函数指针 void func(int a){...} -> T*

函数模板重载

函数模板之间,函数模板与普通函数之间可以重载。编译器会根据调用时提供的函数参数,调用能够处理这一类型的最特殊的版本。在特殊性上,一般按照如下顺序考虑:

  1. 普通函数

  2. 特殊模板(限定了T的形式的,指针、引用、容器等)

  3. 普通模板(对T没有任何限制的)

对于如何判断某个模板更加特殊,原则如下:如果模板B的所有实例都可以实例化模板A,而反过来则不行,那么B就比A特殊。

template<typename T>
void func(T& t) { //通用模板函数
cout << "In generic version template " << t << endl;
} template<typename T>
void func(T* t) { //指针版本
cout << "In pointer version template "<< *t << endl;
} void func(string* s) { //普通函数
cout << "In normal function " << *s << endl;
} int i = 10;
func(i); //调用通用版本,其他函数或者无法实例化或者不匹配
func(&i); //调用指针版本,通用版本虽然也可以用,但是编译器选择最特殊的版本
string s = "abc";
func(&s); //调用普通函数,通用版本和特殊版本虽然也都可以用,但是编译器选择最特化的版本
func<>(&s); //调用指针版本,通过<>告诉编译器我们需要用template而不是普通函数

模板函数特化

有时通用的函数模板不能解决个别类型的问题,我们必须对此进行定制,这就是函数模板的特化。函数模板的特化必须把所有的模版参数全部指定。

template<>
void func(int i) {
cout << "In special version for int "<< i << endl;
} int i = 10;
func(i); //调用特化版本

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lezardfu/article/details/56852043

Template 基础篇-函数模板(待看的更多相关文章

  1. C++ template学习一(函数模板和模板函数)

    函数模板和模板函数(1)函数模板函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计.它的最大特点是把函数使用的数据类型作为参数.函数模板的声明形式为:templat ...

  2. template学习一函数模板

    要点: 1.模板参数在实体化的时候不能自动类型转换,只有非模板函数才可以 例如: int max(int,int); template <class T> T max(T,T); 在调用的 ...

  3. SQL基础篇---函数及其函数配套使用的关键字

    一.数值函数 知识点1 SUM 求总和 SELECT breakfast,sum(price) FROM my_foods GROUP BY breakfast ORDER BY SUM(price) ...

  4. C++ 函数模板基础知识

    为什么要引入模板:为了避免代码重复,程序员可以编写脱离数据类型通用模板. 模板的分类:函数模板 + 类模板 注意:模板的声明或定义只能在全局,命名空间或类范围内进行.不能在函数内进行,比如不能在mai ...

  5. [转]C++函数模板与模板函数

      1.函数模板的声明和模板函数的生成   1.1函数模板的声明 函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计.它的最大特点是把函数使用的数据类型作为参数. ...

  6. C++模板学习:函数模板、结构体模板、类模板

    C++模板:函数.结构体.类 模板实现 1.前言:(知道有模板这回事的童鞋请忽视) 普通函数.函数重载.模板函数 认识. //学过c的童鞋们一定都写过函数sum吧,当时是这样写的: int sum(i ...

  7. C++复习:函数模板和类模板

    前言 C++提供了函数模板(function template).所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表.这个通用函数就称为函数模板.凡是函数体 ...

  8. [c++][语言语法]函数模板和模板函数 及参数类型的运行时判断

    参考:http://blog.csdn.net/beyondhaven/article/details/4204345 参考:http://blog.csdn.net/joeblackzqq/arti ...

  9. C++:函数模板与模板函数

    6.1 模板的概念 C++允许用同一个函数定义函数,这些函数的参数个数和参数类型不同.例如求最大值的max函数, int max(int x,int y) {       return (x>y ...

随机推荐

  1. 搭建Windows IIS(Internet Information Server)服务器

    1.找到 控制面板\程序 点击 “打开或关闭Windows功能” ,全部选上“Internet信息服务”,这里可能要等几分钟. 2.完成之后你打开C盘,在他的根目录下多出一个inetpub文件夹里面有 ...

  2. according to tld or attribute directive in tag file attribute *** does not accept any expressions

    http://stackoverflow.com/questions/13428788/according-to-tld-or-attribute-directive-in-tag-file-attr ...

  3. 寒假作业第二组C题题解

    这道题题意很简单,主要是练习map的使用.看输入有三个数据,水果名,地名,和出现次数.再看输出,很容易想到map<string,int> string是水果,int是次数,那个地名怎么用m ...

  4. proxy_pass http://127.0.0.1:5000; 502 bad getway

    (13: Permission denied) while connecting to upstream:[nginx] I am working with configuring django pr ...

  5. Net Core2.0下使用Dapper

    Net Core2.0下使用Dapper 今天成功把.Net Framework下使用Dapper进行封装的ORM成功迁移到.Net Core 2.0上,在迁移的过程中也遇到一些很有意思的问题,值得和 ...

  6. 5. 把一幅彩色图像的H、S、I分量单独显示。

    #include <cv.h> #include <highgui.h> # include <math.h> #define M_PI 3.1415 void R ...

  7. C#中的XML文档注释-推荐的文档注释标记

    文档注释是为了方便自己和他人更好地理解代码所实现的功能.下面记录了一些常用的文档注释标记: <C> 用法: <c>text</c> 将说明中的文本标记为代码.例如: ...

  8. 天上掉馅饼 期望DP

    C 天上掉馅饼文件名 输入文件 输出文件 时间限制 空间限制bonus.pas/c/cpp bonus.in bonus.out 1s 128MB题目描述小 G 进入了一个神奇的世界,在这个世界,天上 ...

  9. js动态更换img的src问题

    在本地开发测试过程中,通过js动态更换img的src没有问题,图片正常切换,但是放在服务器上后测试发现,图片不显示,解决方法为:在对应onclick事件执行切换图片的js函数后加上一个return f ...

  10. 【Java】JMM内存模型和JVM内存结构

    JMM内存模型和JVM内存结构 JAVA内存模型(Java Memory Model) Java内存模型,一般指的是JDK 5 开始使用的新的内存模型,主要由JSR-133: JavaTM Memor ...