原本在处理函数模板特化的问题
template <class T>
bool IsEqual(const T& left,const T& right)
{
return left == right;
}
这里我设计了一个模板来判断不同类型的值是否相等,
考虑到节省拷贝时间,同时又不想让函数修改实参,函数的形参设计为常量引用 👉const T&
。
ok,没什么问题。
但是如果面对字符数组,单纯的==
只能比较指针的大小,完全考虑不到字符数组的内容,说明我们的模板覆盖的范围还不够。
所以就字符数组的问题还需特化一个函数模板,其中使用strcmp
函数,用来专门处理C字符串。
我想了下很简单嘛,把T
换成char*
,然后用strcmp
来判断不就行了?
于是写出了字符数组函数模板的特化版本:
//针对字符串类型的特殊化处理--写一份函数模板的特化
template<>
bool IsEqual<char*>(const char*& left,const char* right)//const 修饰的是引用
{
return strcmp(left,right)==0;
}
这里是对指针的常量引用,但是一写完我就觉得怪怪的,果然还没运行,编译器就出现了报错:
一开始我以为是把函数模板特化的格式写错了,但是换了其他的类型之后,发现不是这里的问题。
于是我估计是指针的常量引用写错了,于是做了一小实验看一下,指针的常量引用到底是不是这么写的。
- 第一步:先试下普通的指针引用,在这个函数里既可以修改指针指向的值(通过指针解引用),又可以修改指针本身(通过引用)
void print1(int*& p)
{
cout << typeid(p).name() << endl;
}
int main()
{
int* a;
print1(a);
}
结果:一切正常。
- 第二步:按照模板特化的写法,改成指针的常量引用(言下之意就是说:我们不可以在函数中修改指针的指向)。
void print1(const int*& p)
{
cout << typeid(p).name() << endl;
}
int main()
{
int* a;
print1(a);
}
果然,我找到了痛点!!
编译报错了:
error C2664: “void print1(const int *&)”: 无法将参数 1 从“int *”转换为“const int *&”
问题思考:为什么int*
无法转换给const int*&
?
- 第三步 先理解
const int*
和int* const
int main()
{
int b=10;
int* pb=&b;
int *const p0=pb;
const int* p1=pb;
return 0;
}
在常量指针的设计中,const的摆放位置很有讲究,
const
放在*
前,是底层const,“锁”住的是指针指向的值,即该指针无法修改其指向的值。const
放在*
后,是顶层const,“锁”住的是指针本身,即该指针无法修改其自身的值,但是可以修改其自身指向的值。
p0 不能修改,但是 *p0 可以修改。
*p1不能修改,但是 p1可以修改。
- 第四步 理解
const int*&
和int* const&
(重点)
const int*&
首先是一个普通引用,而且引用的类型是const int*
。int* const&
则是常量引用,引用类型是int*
。
ok,类型分清楚了,看能不能区分下面p2~p5引用的初始化对错
int main()
{
int b = 10;
int* pb = &b;
int* const p0 = pb;//“锁指针,不锁指向值”
const int* p1 = pb;//“锁指向值,不锁指针”
//区分正误
int* const& p2 = p1;
int* const& p3 = pb;
int* const& p4 = p0;
const int*& p5 = pb;
return 0;
}
-
p2的情况
p1类型——
const int*
p2类型——int* const&
p2是p1的常量引用,始终保证指向p1,而且无法用p2来修改p1的值,这里的值是指针本身。
但是p2有权限可以修改指针指向的值,而p1又锁住了修改指针指向的值的权限,故权限被放大了!p1不会允许p2有这样的越权行为,故错误的初始化❌。
-
p3的情况
p3和p2的类型一致
int* const&
,这里改用pb初始化。pb的类型是int*
pb的类型是int*,p3始终追随pb,而且p3(常量引用)无法修改pb的值。
p3具有修改指向值的权限,pb谁都没锁柱,当然不会越权了。这里是正确的✔
-
p4的情况
p4依旧是
int* const&
类型,这里用类型为int *const
的p0初始化。p0不能修改自身值,p4是常量引用也无法修改p0的值,恰如其分。
p0可以通过指针修改指向值,p4也可以通过指针修改指向值,恰如其分。
这样就是完全匹配的类型了,是正确的初始化✔。
-
p5的情况
p5类型——
const int*&
pb类型——int*
这里就来到了我们的问题根本。
p5紧紧跟随pb,p5只是普通引用,可以改变pb的指针值,这都ok。
但是! p5指向的值是没有办法修改的,而pb有修改指针指向值的能力
很显然,这样p5的
const int*
没有任何意义,调用p5的程序员设想的是:使用的p5指向的是一个无法改变的常量,但是这个“常量”却始终有可能被 *pb 胡乱变动,这不是瞎胡闹吗~引用被变量"背刺"了,编译器表示我不允许!
正确处理:
类型完全匹配就可以正常使用引用了。
p1——const int*
p5——const int*&
针对指针的引用:
-
要么都不能对指向值修改
锁指向值的指针,就用锁指向值的指针的普通引用或锁住指向值的指针的常量引用。
🆗:const int*
和const int*&
🆗:const int*
和const int* const &
-
要么都能对指向值修改
🆗:int*
和int* &
🆗:int*
和int* const &
-
注意⚠:
锁指针值的指针,就必须使用常量引用。
int const*
不能使用int*&
,这里引用是可以修改锁住的指针的。需使用int* const&
ok,回到我起初的函数模板特化,
完美运行。