文章目录

  • ​​1.模板​​
  • ​​2.函数模板​​
  • ​​3.函数模板特化​​
  • ​​4.重载函数模板​​
  • ​​5.非模板函数重载​​

1.模板

  • 考虑求两数较大值函数max(a,b)
  • 对于a,b的不同类型,都有相同的处理形式:
return a < b ? b : a;
  • 用已有方法解决:
    (1)宏替换 #define max(a,b) ((a)< (b) ? (b) : (a))
    存在的问题:避开类型检查
    (2)重载
    存在的问题:需要许多重载版本,当新增一个函数类型,需要求2个数的最大值时,需要提供一个新的重载版本,不方便扩展
    (3)使用函数模板:结合了宏替换和重载的优势,重载是函数,会做安全类型检查
    为相同逻辑的代码提供模板,当处理不同数据类型时,可以将数据类型当作是参数来传递,让编译器实例化对应版本的函数来处理(让编译器实例化对应版本的函数来处理可以理解为宏替换的过程)。
    程序员只是维护模板,而不同的版本的函数由编译器来维护
  • 模板是一种参数化的多态工具
    这些函数在编译器就决定,是一种静态的多态,与重载一样。虚函数的多态只有一种就是虚函数的多态。
    eg:vector,auto_ptr都是以类模板的方式提供的。
  • 所谓参数化的多态性,是指将程序所处理的对象的类型参数化,使一段程序代码可以用于处理不同类型的对象。
  • 采用模板编程,可以为各种逻辑功能相同而数据类型不同的程序提供一种代码共享的机制。
  • 模板的分类
    (1)函数模板(function template)
    (2)类模板(class template)

2.函数模板

  • 函数模板的一般说明形式如下:
template < 模板形参表>
返回值类型 函数名(模板函数形参表)
{
//函数定义体
}
  • 函数模板的定义以关键字template开头
  • template之后<>中是函数模板的参数列表
  • 函数模板的参数是类型参数,其类型为class或typename(推荐使用typename)
template<class T>
template<class T1, class T2>

模板形参表可以有多个
  • 模板形参在模板中作为一种类型使用,可以用于函数的形参、函数返回值和函数的局部变量
  • 每个模板形参要在函数的形参列表中至少出现一次
  • 模板参数名的作用域局限于函数模板的范围内
  • 函数模板的使用
    (1)函数模板为所有的函数提供唯一的一段函数代码,增强了函数设计的通用性
    (2)使用函数模板的方法是先说明函数模板,然后实例化成相应的模板函数进行调用执行
    函数模板不是函数,不能被执行
    置换代码中的类型参数得到模板函数——实例化
    实例化后的模板函数是真正的函数,可以被执行
    实例化的过程是在编译的时候完成的(由编译器完成)
  • (P68)模板一:模板,函数模板,函数模板特化,重载函数模板,非模板函数重载_函数模板

  • (3)模板被编译了两次
    实例化之前,先检查模板代码本身,查看语法是否正确;在这里会发现语法错误,如果遗漏分号等。
    实例化期间,检查模板代码,查看是否所有的调用都有效。在这里会发现无效的调用,如该实例化类型不支持某些函数调用或操作符等。
    (4)普通函数只需要声明,即可顺利编译,而模板的编译需要查看模板的定义(声明和定义需放在同个.h文件)。因模板在编译的时候,需要自动推导,自动推导的函数还需要校验里面的调用是否合法,若编译时,只能看到声明,不能看到实现,则无法进行实例化(链接会出错,编译没错)。
  • 函数模板可以通过传递的参数类型自动推导,查看是否有合适的函数实例可用,而类模板则必须显式说明模板的类型参数,这样才能实例化模板类实例。

3.函数模板特化

  • 假设现在我们有这样一个模板函数max:
template <typename T>
const T& max(const T& a, const T& b)
{

return a < b ? b : a;

}

然后现在我们要比较两个字符串的大小,如:

const char* str1 = "aaa";
const char* str2 = "zzz";
cout<<::max(str1, str2)<<endl;
此时如果按一般的实例化,比较的将是str1 和 str2 的大小,即比较指针大小,而不是字符串内容大小,故我们需要实现一个模板函数的特化,

(P68)模板一:模板,函数模板,函数模板特化,重载函数模板,非模板函数重载_函数模板_02

  • 如下:
template<>
const char* const& max(const char* const& a, const char* const& b)
{

return strcmp(a, b) < 0 ? b : a;

}

4.重载函数模板

  • C++语言可以重载一个函数模板
  • 用户可以用非模板函数重载一个同名的函数模板
  • eg:
    P68\max.h
#ifndef _MAX_H_
#define _MAX_H_
#include <iostream>
using namespace std;

//模板的定义通常放在头文件中
//不要将模板的声明放在头文件中,实现放在cpp文件中,一般不要分开放
template < typename T>
const T &max(const T &a, const T &b)
{
return a < b ? b : a;
}


// 函数模板重载
//参数不同,构成重载
template < typename T>
const T &max( const T &a, const T &b, const T &c)
{
return ::max(a, b) < c ? c : ::max(a, b);
// ::max表示调用的是全局的,而不是标准库的
}

// 非模板函数重载
//与模板const T &max(const T &a, const T &b)构成了重载
const int &max( const int &a, const int &b)
{
cout << "non template function" << endl;
cout<<"BBBBBBBBBBBBBBB"<<endl;
return a < b ? b : a;
}

//函数模板特化
template <>
const char * const &max( const char * const &a, const char * const &b)
{
//return a < b ? b : a;
cout<<"AAAAAAAAAAAAA"<<endl;
return strcmp(a, b) < 0 ? b : a;//比较的是内容,而不是指针本身
}

/*
模板特化与模板函数的区别:
模板特化本质上属于模板函数,非模板函数重载不算是模板函数,他是全局函数

*/
#endif // _MAX_H_

P68\01.cpp

#include <iostream>
#include <string>
using namespace std;

#include "max.h"
// template < typename T>
// const T &max(const T &a, const T &b)
// {
// return a < b ? b : a;
// }

//编译完后,编译器会实例化这样一个模板函数
//是由编译器推导的,叫deduction
// const int& max(const int &a, const int &b)
// {
// return a < b ? b : a;
// }

// class Test
// {

// };

class Test
{
public:

friend bool operator<(const Test &t1, const Test &t2)
{
return true;
}
};

int main( void)
{
//::max(5.5, 6.6)表示调用的是全局的max函数,而不是std中命名空间的max函数
cout <<::max( 5. 5, 6. 6) << endl; // 自动推导 max(const double&, const double&);
cout <<::max( 'a', 'c') << endl; // 自动推导 max(const char&, const char&);


Test t1;
Test t2;
::max(t1, t2); // Test::operator<(const Test& t1, const Test& t2)
//编译error,因为Test类对象不支持<运算
//所以需要重载<运算符

const char *str1 = "aaa";
const char *str2 = "zzz";
cout <<::max(str1, str2) << endl; //优先选择非模板函数,若找不到,才考虑模板函数

out <<::max<>(str1, str2) << endl; //指定使用模板,进而找到模板特化

cout <<::max( 1, 5, 3) << endl; // 模板匹配,进而自动推导

cout <<::max( 'a', 100) << endl; // 'a'即97;选择非模板函数(char可以隐式转换成int)

cout <<::max( 97, 100) << endl; // 优先选择非模板函数
//并不是调用模板推导出的函数,而是调用的是重载的非模板函数

cout <<::max<>( 97, 100) << endl; //调用模板推导出的函数
//<>表示让模板推导

cout <<::max< int>( 97, 100) << endl; // 显式指定模板函数max(const int&, const int&),不是自动推导,而是指定的
cout <<::max< int>( 'a', 100) << endl; // 显式指定模板函数max(const int&, const int&)
return 0;
}
  • 测试:

5.非模板函数重载

  • 见上面的eg
  • 参考:从零开始学C++之模板(一):函数模板、函数模板特化、重载函数模板、非模板函数重载