考虑如下代码:
1 void g(int* i){ }
2
3 int main(){
4
5 g(0); //ok 调用g((int*)0)
6
7 const int x = 0;
8 g(x); //ok 调用g((int*)0)
9
10 int x=0;
11 g(x); //error. 编译错误,int无法转换int*
12
13 return 0;
14
15 }
文字量0的类型,永远是int
编译期int常量0,在遇到转换成指针的场合,会被隐式转换成指针。其它常量不会,1,2,3...都没有这个待遇
0值得这个特殊待遇,遇到变量就会不好使了。例如:第11行,变量x,即使其值为0也没用。这个规则会延续到模板,例如:
template<class PointerType>
void template_call( PointerType x ){
g(x);
}
void g(int* i){}
int main(){
template_call(0);
}
这相当于,调用的是:
void template_call( int x )
{
g(x); //11行一样的失败
}
第一个解决方法是:手动写 template_call( (int)0); 也没什么缺点,就是需要程序员去查勘g(int)的参数类型,然后回来把int*拷贝到0的前面。实际上指向任意类型的万能空指针破产了,这是优秀(懒惰)的程序员不能容忍的。因此第二个方案nullptr引入了。
namespace study {
const class nullptr_t
{
public:
template<class T>
inline operator T*() const //隐式转化
{
return 0;
}
template<class C, class T> //隐式转化
inline operator T C::*() const
{
return 0;
}
void operator&() const = delete;
} ;
}
template<class PointerType>
void template_call( PointerType a ){
g(a);
}
void g(int* i){ }
int main(){
//代替:template_call((int*)0)
template_call( study::nullptr_t{} );
}
template_call( study::nullptr_t{}) 实例化模板参数PointerType=study::nullptr_t
这样g( study::nullptr_t{} );匹配不了g(int)啊!!但是我们看到有个nullptr_t到int的隐式转换:
const class nullptr_t
{
public:
inline operator int*() const //隐式转化
{
return 0;
}
这样g( study::nullptr_t{}.operator int() ); 就是 int tmp=0; g(tmp);的展开结果了。万能空指针又回来了。
f( study::nullptr_t{}); 在标准库std名字空间里是f( std::nullptr_t{} ); 但是还是没有0简洁啊!!
常规思路可能定义个全局对象,例如constexpr std::nullptr_t nullptr; 然后,就简化成f( std::nullptr ); 但是前面那个std::五个字符还是太复杂!!
nullptr就不再是个全局对象了,而被收录成关键字!!就像for ,class一样的。这下好了,std::也省了。就成了f(nullptr)
f( nullptr ) 比 f(0)多敲打6个字符,但是比起f(NULL)只多敲打了3个字符,好像还说得过去。如果把nullptr改成np为关键字,则f(np)是不是更省键盘呢?
原文链接: https://www.cnblogs.com/thomas76/p/8592904.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/270519
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!