本篇主要讨论函数模板的相关知识点。
前言:
"typename"
是一个C++程序设计语言中的关键字。当用于泛型编程时是另一术语"class"
的同义词。这个关键字用于指出模板声明(或定义)中的非独立名称(dependent names)
是类型名,而非变量名。
"typename"
的作用就是告诉 c++ 编译器,typename
后面的字符串为一个类型名称,而不是成员函数或者成员变量,这个时候如果前面没有 typename
,编译器没有任何办法知道 T::LengthType
是一个类型还是一个成员名称(静态数据成员或者静态函数)。请参考C++typename的由来和用法
总结:
1.什么是模板?使用模板的原因?
-
模板是为了解决函数在使用过程中
只是数据类型发生变化,而内容未发生变化
,这个时候就可以使用模板来表示多种数据类型的状况
。 -
模板–核心:把某些类型(
int double...
)的关键字看成是变量(函数重载的泛化
) -
打个比方,同样的凉拌菜方法,你可以用来凉拌个黄瓜,也可以凉拌个猪耳朵(可变的就是黄瓜和猪耳朵)。
2.函数模板的定义:
template<typename T>
T GetMin(T a, T b)
{
return a > b ? a : b;
}
3.函数模板隐式实例化及存在的问题
- 函数模板隐式实例化:告诉编译器函数的参数类型,编译器猜测函数模板的类型
int n = GetMin(1, 1);
- 存在问题:
GetMin(1,2.0f)
就会让编译器不知道该找哪一个,是int
还是float
类型呢?这个时候就会导致二意性
4.函数模板显式实例化: 通过GetMin<int>(1, 2.0f);
的方式指明参数和返回值默认为int
数据类型
5.函数模板的特例: 当需要针对某个特定类型写一个模板
6.注意: 在函数模板真正实例化之后,模板才会起到作用
我们在写代码时可能经常会碰到下面这种情况,比较两个数的大小,功能都是一样的,但是参数类型却不一样。
#include <iostream>
using namespace std;
//在内容不改变,只是变量类型有不同的时候适用模板
int GetMin(int a, int b)
{
return a > b? a: b;
}
int GetMin(float a, float b)
{
return a > b ? a : b;
}
int main(int argc, char* argv[])
{
cout << "int GetMin(int a, int b)" << GetMin(1, 2) << endl;
cout << "int GetMin(float a, float b)" << GetMin(1.14f, 2.14f) << endl;
return 0;
}
运行结果如下:
此时如果需要比较两个double类型的数据该如何去做呢?按照之前所学我们仍然需要再写一个以double类型数据作为参数的函数,我们此时就成为了人肉复制机。类似的想要比较string、long…等数据类型呢?
如果编译器可以帮助我们重复上面的动作,这样的话就可以省很多操作,模仿日常生活中处理重复枯燥的工作,可以先准备一个模板,在程序中写出一个大体的框架写好,指导编译器修改框架中的少部分内容,从而让它产生更多的函数,这就是模板。
1.什么是模板?使用模板的原因?
- 模板是为了解决函数在使用过程中
只是数据类型发生变化,而内容未发生变化
,这个时候就可以使用模板来表示多种数据类型的状况
。 - 模板–核心:把某些类型(
int double...
)的关键字看成是变量(函数重载的泛化
)
打个比方,同样的凉拌菜方法,你可以用来凉拌个黄瓜,也可以凉拌个猪耳朵(可变的就是黄瓜和猪耳朵)。
2. 函数模板的定义
模板的具体写法如下:
#include <iostream>
using namespace std;
//模板--核心:把某些类型(int double...)的关键字看成是变量(函数重载的泛化)
template<typename T>
T GetMin(T a, T b)
{
return a > b ? a : b;
}
int main(int argc, char* argv[])
{ return 0;}
3. 函数模板隐式实例化及存在的问题
函数模板的隐式实例化:告诉编译器函数的参数类型,编译器猜测函数模板的类型
具体使用方法如下:
#include <iostream>
using namespace std;
//模板--核心:把某些类型(int double...)的关键字看成是变量(函数重载的泛化)
template<typename T>
T GetMin(T a, T b)
{
return a > b ? a : b;
}
int main(int argc, char* argv[])
{
//函数模板的隐式实例化(由编译器猜测函数模板的类型)
int n = GetMin(1, 1);
float f= GetMin(1.0f, 2.0f);
return 0;
}
但是隐式实例化可能会导致编译器在选择匹配函数时陷入两难境界。例如:GetMin(1,2.0f)
就会让编译器不知道该找哪一个,是int
还是float
类型呢?这个时候就会导致二意性,可以采用**显式实例化
**来指定数据类型。
4. 函数模板显式实例化
通过
GetMin<int>(1, 2.0f);
的方式指明参数和返回值默认为
int
数据类型,其书写可以从
template<typename T>
变化而来,template被具体的GetMin函数名替换,typename T用具体的int替换
#include <iostream>
using namespace std;
//模板--核心:把某些类型(int double...)的关键字看成是变量(函数重载的泛化)
template<typename T>
T GetMin(T a, T b)
{
return a > b ? a : b;
}
int main(int argc, char* argv[])
{
//显式实例化,指明数据类型
int n=GetMin<int>(1, 2.0f);
return 0;
}
运行结果:
int main(int argc, char* argv[])
{
//显式实例化,指明数据类型
float f=GetMin<int>(1, 2.1);
return 0;
}
运行结果:返回值为float
但是比较时是按照Int
进行的,float f=GetMin<int>(1, 2.1);
有强转的意思
5. 函数模板的特例
前面的均为数值的比较,并不能覆盖所有的数据类型,如果需要比较两个字符串该怎么做呢?
采用以下的方式,比较的是两个字符串在编译时分配的地址高低,没有任何意义
#include <iostream>
using namespace std;
template<typename T>
T GetMin(T a, T b)
{
return a > b ? a : b;
}
int main(int argc, char* argv[])
{
GetMin("Hello", "World");
return 0;
}
当需要针对某个特定类型写一个模板
,就称为模板的特例化
#include <iostream>
using namespace std;
template<typename T>
T GetMin(T a, T b)
{
return a > b ? a : b;
}
//当函数模板不够用,可以采用模板特例化的形式
//模板的特例化
template<>
char* GetMin<char*>(char* a,char* b)
{
return a;
}
int main(int argc, char* argv[])
{
//模板的特例化,必须使用显示实例化调用
GetMin<char*>("Hello", "World");
return 0;
}
另外需注意:在函数模板真正实例化之后,模板才会起到作用
6.学习视频地址:C++57个入门知识点_55 函数模板