一.区分define以及typedef:
首先一点,大家都可以答得上来,define在预编译阶段处理,typedef在编译阶段处理.
来看下面的这个例子:
明确一点:const修饰的是离它最近的类型
#define T int
typedef int T;
const T a;
在这里没什么区别,但是当const后面有*的时候就很明显了.
#define T char*
typedef char* T;
const T b;
不论是define还是typedef最终看到的是const char* b;
对于define:
尽管在预编译时经过字符串替换后到达编译阶段编译器看到就是const char*b
但是编译器不会将char*看做是一个整体,在与const结合的时候自然会和char结合,
所以,编译器保护的是*b
对于typedef:
对于typedef就不一样了,他是类型替换而不是字符替换,它在编译阶段发生的
编译阶段编译器看到的就是const T b,而T正是我们char*
所以,当与const结合的时候,编译器自然修饰的是T,也就是char*,保护的自然是b
所以,总结一句话,define和我们平时见到的const与指针结合时规范是一样的,修饰的是离const最近的类型,但是typedef不一样,编译器会将重定义的类型看做是一个整体,不会将这个整体再去拆分,这就是区别.
使用typedef时需要注意一种情景:
在我们c/c++有一种类型叫做unsigned int,实际上这是两个类型的组合,不对吗?
1 #include <iostream>
2 using namespace std;
3
4 typedef int T;
5 int main()
6 {
7 unsigned T val = 0;
8 return 0;
9 }
如果你看到这篇文章,你可以去试一下上面代码能不能编译通过?
如果编译通过了,那意思就是编译器允许类似于这样double int val = 100 ????
自然是不行的啦
#include <iostream>
using namespace std;clear
#define T int
int main()
{
unsigned T val = 0;
return 0;
}
这样就可以顺利编译通过了.
因为预编译阶段unsigned T 和我们自己定义时写的unsigned int没区别,这样自然是支持的
二.函数模板的实例化时传递类型给模板参数列表的时候是typedef的过程还是define:
#include<iostream>
using namespace std;
template<typename T>
bool com(const T a, const T b)
{
cout << "template<typename>" << endl;
return a > b;
}
int main()
{
com<char*>("hello","world");
return 0;
}
为什么需要特例化:
对于字符串的比较来说,我们肯定不能通过比较字面值判断字符串的大小,否则成比较地址大小了 ~. ~,函数模板已经不能满足我们的对于char类型的需求了,这个时候需要一个模板针对char的特例化版本。
template<>
bool com<char*>(const char*a, const char*b)
{
cout << "template<>" << endl;
return strcmp(a,b)>0 ? true:false;
}
然后,我们去编译,编译器报错:
“bool (__cdecl *com(char *))(const char *,const char *)”不是函数模板的专用化
原因是这样的:
原函数模板:
template<typename T>
bool com(const T a, const T b)
在原模板中,const修饰的是a和b
模板特例化版本:
template<>
bool com<char*>(const char*a, const char*b)
在特例化版本中,const 修饰的是*a和*b 这样一来,特例化版本就违背了原模板的初衷
原模板保护了a和b,特例化保护了*a和*b,自然编译不过
修改正确,是的const保护a和b,而不是带*的a和b:
//正确的显式专用化
template<>
bool com<char*>(char* const a, char* const b)
{
cout << "template<>" << endl;
return strcmp(a,b)>0 ? true:false;
}
总结:
- 实例化点类型到模板参数类型的替换是一个typedef的过程,不论T是什么类型,int,float还是char*,传递过去一定是typedef的过程,如果有const修饰,一定修饰的是T这个整体。
- 保证特例化中原型的规则和原模板原型的规则一致。
补充一点:
- 不管是const还是volitle,都是一样的规则,因为volitle也是构成重载的要素;
- 不管是类模板还是函数模板,原模板中const修饰是谁,在特例化中就要注意,不能将const修饰的改变。