Effective_C++:25、避免对指针型别和数值型别进行重载
程序员文章站
2022-07-15 12:24:04
...
25、避免对指针型别和数值型别进行重载
1、指针型别和数值型别进行重载的问题
指针保存的是其所指对象的地址,解析时将其变为数值类型。尤其是有时我们把一个空指针定义为0,则当传入一个值为0的参数,若对指针型别和数值型别都进行重载的函数,应当出现编译错误,因为此时编译器不知道0是指针型别还是数值型别。然而,实际上,编译器对于入参为0的情况,会认为其为数值型别,会调用参数为数值型别的函数,并不会出现编译错误。
void f(int x);
void f(string *ps);
2、尝试解决问题
为此,我们尝试定义一个常量,使其可以转换为指针型别和数值型别,则当函数参数为此常量时,即可表现出二义性。
1.如下,NULL可以转换为指针型别,然而需要显示类型转换,即调用类型转换操作符实现。而且调用数值型别的重载函数失败。
void * const NULL = 0;//void*指针可以转换为各类型指针,其本身定义为0
f(NULL);//错误,型别不符
f(static_cast<string *>(NULL);//成功,调用f(string *);
2.尝试用宏定义。在调用指针型别的重载函数时仍需要显示类型转换。
#define NULL 0
#define NULL ((void*) 0)
f(NULL);//成功,调用f(int);
f(static_cast<string *>(NULL));//成功,调用f(string *);
3.一个临时的解决办法。long int 0转换为int与null指针是相同的,不分优先、好坏。此时似乎可以实现让编译器编译错误。然而,当有一个f(long int)与f(string *)重载函数时,此方法就失效了。
#define NULL 0L
f(NULL);//错误,二义性
4.为此,我们希望定义这样一个类,其可以实现将对象隐式转换为所有类型指针,则当调用函数f传入该类对象时,可以将对象隐式转换为任意类型的指针,即解决了上述1、2中需要进行显示类型转换的问题。此时,引入成员函数模板,产生该class的一些成员函数。
class NullClass {
public:
template<class T>
operator T*() const { return 0; }
};
const NullClass NULL;
优化一下,没必要给出class名称,不需要一个以上的类对象;添加可使其隐式转换为指向类成员的指针的成员函数模板;最后,NULL是指针值,不允许取地址。
const
class {
public:
template<class T>
operator T*() const
{ return 0; }
template<class C, class T>
operator T C::*() const
{ return 0; }
private:
void operator&() const;
} NULL;
3、避免对指针型别和数值型别进行重载
以上,我们定义了NULL来实现传入NULL时隐式类型转换为指针型别,来让编译器实现入参的二义性而编译错误。然而,回到最初的问题,若传入0,编译器仍然认为他是数值型别。
因此,最好的办法就是避免对指针型别和数值型别进行重载。