“为什么会这样?!”,zero 一边喝水一边嘟囔着,恨恨的看着面前显示器上的代码,“为什么这么简单的一个调用也会出现编译错误 …… ”

“这是因为你的设计太差!”

噗!zero 被幽灵一样出现在背后的 Solmyr 吓了一大跳,一口水差点全喷出来。

“咳!咳咳!S …… Solmyr ,你什么时候站在我背后的?”,zero 很费力的平息了咳嗽,同时努力回想刚才自己有没有把柄会被 Solmyr 抓到。

Solmyr 抓过一张椅子坐了下来:“在你一开始干傻事的时候我就在了,正是这个糟糕的设计导致了现在困扰你的编译错误。”

“哪 …… 哪里?”

“这儿。” Solmyr 抓过键盘,标出了下面这段代码:

void SomeFunc(int i)
…………

void SomeFunc(float f)
…………

int main()
{
…………
SomeFunc(1.2);// Error! ambiguous call
…………
}

“我也正觉得奇怪”,zero 一如既往的挠着头,试图压榨不存在的智慧,“这么简单的一个函数重载,应该很清楚才对。我这里调用时明明给出的是浮点数,显然应该调用 float 版本的 SomeFunc 。最奇怪的是如果没有这个调用,整个程序编译连接完全没有问题,可见这样重载函数是合法的。”

“嗯,没错,确实是合法的,但是合法不代表正确。zero ,你念一下这一段,看看先知 Meyers 在他的《50 诫》(注:指《Effective C++ 2/e》一书)中的条款 26 中是怎样描述 C++ 对待‘模棱两可’的哲学的。”,Solmyr 翻开了一本书,指着其中的几行。

“C++ ……”

“站起来,大声念!”

zero 依言站起,中气十足的念道:“C++ 也有一个哲学信仰:它相信潜在的模棱两可的状态不是一种错误。”

旁边的座位上传来低低的窃笑声,更远处的人探头张望,投来好奇的目光,zero 顿时感到自己像个傻瓜。当 zero 看到 Solmyr 嘴边招牌式的坏笑时明白了过来:自己又一次被 Solmyr 设计了。

“嗯,明白了这一点,我们就可以展开进一步的讨论了”,Solmyr 开始转入正题,“还记得上次我说过上面的 1.2 是什么吗?”

zero 露出了回忆的表情:“嗯 …… 1.2 是‘写在代码里的常量’…… 应该是一个 double 类型常量。”

“这就是问题所在:编译器看到这个调用函数的请求,会去寻找你的重载函数中哪个函数能够匹配这个调用请求给出的参数,结果它发现没有一个函数的参数是 double 类型的,所以必须要做类型转换,但是 double 类型既可以转成 int ,也可以转成 float ,究竟转哪个好呢?编译器不知道,所以只好报错了。明白了吗?”

zero 似懂非懂的点了点头。

“那我问你,这样重载编译时会不会报错?”,Solmyr 稍稍改动了一下 zero 的代码:

void SomeFunc(int i)
…………

void SomeFunc(double db)
…………

int main()
{
…………
float f = 1.2;
SomeFunc(f);
…………
}

zero 看了看,学着 Solmyr 的语气说到:“编译器发现没有一个函数的参数是 float 类型的,所以必须要做类型转换,但是 float 类型既可以转成 int ,也可以转成 double ,究竟转哪个好呢?编译器不知道,所以只好报错了。”

“错!”,Solmyr 顺手按下了运行按钮,程序运行一切正常,输出显示调用的是 double 版本的 SomeFunc 函数。

zero 再度感到了困惑:“为什么同样是要选择类型转换,这个就没错,前一个就有错呢?这中间的逻辑何在?”

“重要的是这一句:‘究竟转哪个好呢?编译器不知道’。你没有注意到我说这句话的时候‘好’字上用了一个重音吗?”

“你用过重音吗?”

“ …… 这个不是重点。重点在于,float 到 int 和 float 到 double 这两个转换,编译器是能够选择的,因为 float 到 int 会损失数据 —— 象样的编译器会在做这种类型转换的时候给出一个 warning —— 而 float 到 double 则不损失数据,所以编译器知道‘转哪个好’。而之前的情况,double 到 int 到 float 的转换都要损失数据,所以编译器不知道‘转哪个好’,它没办法做一个决定 —— ”,Solmyr 看了看 zero ,再度问道,“明白了吗?”

zero 皱着眉头,挠头挠的更起劲了,显然对于消化一下子出现的这么多信息感到少许困难:“我想我明白了,关键是编译器能否区分两个类型转换。在这里区分的关键是类型转换是否损失数据,嗯 …… 所以我只要在所有用到浮点数的场合都使用 double 类型,就不会有问题,即使别人用 float 来调用也一样。”

“正确。不过‘模棱两可’的问题可不仅仅出现浮点数身上,例如,这样两个重载函数 …… ”,Solmyr 接着键入:

void SomeFunc(double db)
void SomeFunc(char ch)

“如果我用一个整形变量来调用,会出现什么事情?”,Solmyr 扭头盯着 zero。

“呃 …… 编译器同样无法区分 int 到 double 和 int 到 char 这两个类型转换,所以同样会报错。”

“正确。你能够自己举出几个例子吗?”,Solmyr 把键盘递了回去。

很明显的,zero 陷入了沉思,过了一会儿,屏幕上出现了这样几行代码:

// 用 int 调用的话会出错
void fun(char ch)
void fun(int* pi)// 或者其他指针

// 用 int 调用同样会出错
void fun(double db)
void fun(int* pi)// 或者其他指针

“嗯,很好。不过你还是漏了一种重要情况,”,Solmyr 补充道,“就是参数有缺省值的时候:”

// 调用时如果不给参数会出错
void fun(int i=10)
void fun()

“天哪!”,zero 看起来快要崩溃了,“居然有这么多模棱两可的陷阱,这叫我怎样发布我的函数?在文档里写:以下 153 种调用方式将导致编译错误吗?”

“不要这么紧张,”,Solmyr 好整以暇的说到,“重载函数的模棱两可现象不是不能避免的,办法有两个:一是用模板来代替重载,尤其是象你的 SomeFunc 这样 int 型和 double 型处理算法相同的情况;二是如果要用重载的话,尽可能保证函数的参数个数不同。”

“可是如果处理算法不一样,函数需要的参数个数又相同,那该怎么办?”

“很简单,加入‘无用的参数’,象这样:”

void SomeFunc(float db, int)
void SomeFunc(int i)

“第一个函数的第二个参数没有任何作用,所以你可以干脆不给它命名,只要声明一下有这个 int 型参数就可以了。文档里可以这样写:该参数是为今后升级预留的余地,调用时请传入 0 值。”

“ …… 你的文档里大概都是这一类的话吧 …… 啊!好痛!这回又是一本书!”,zero 被 Solmyr 突如其来的袭击击中,发出了悲惨的哀鸣。

“你得感谢先知 Scott Meyers,他的《 50 诫》轻而薄,我手上拿的若是一本教主 Bjarne Stroustrup 的《圣经》(注:指《The C++ Programing Language 3/e》一书,Bjarne Stroustrup 是 C++ 语言的设计者),你现在已经爬不起来了。”,Solmyr 再度披上了修养的伪装,不过言辞中仍然留着一点点杀气的痕迹 ……

“真是残暴的家伙 ……”,zero 小声嘟囔着。

“你说什么?”,杀气再度升高。

“不,不!我什么也没说!”,zero 连忙否认,试着转移话题,“啊!我懂了,要避免模棱两可的陷阱,一是用模板来替代重载,二是利用加入‘无用的参数’这一手段保证重载函数参数个数不同。这样就可以避开模棱两可的问题,是不是,Solmyr 老师?”。zero 很努力的装出天真无邪的样子。

“ …… 真是拙劣的演技 …… ”,Solmyr 心中暗想。“不完全,上述手段只能解决函数重载这一块而已,模棱两可问题涉及的情况要广泛的多,比如《 50 诫》中的例子:”

class B;// 前置声明

class A
{
public:
A(const B&);// A 可以根据 B 构造出来
};

class B
{
public:
operator A() const;// B 可以被转换为 A
};

“这两个类本身没有什么问题,但若是有个函数需要 A 的对象作为参数,传过去的却是个 B 的对象时:”

void f(const A&)
B b;
f(b);// Error! ambiguous call

“注意到这里面的问题了吗?有两种一样好方法可以完成转换,一是用 A 的构造函数以 b 为参数构造一个新的 A 类对象,而是调用 B 的转换函数将 b 转换为一个 A 类对象。编译器再度无法区分哪个转换更好,只能报错了。后面还有一个多重继承的例子,你自己看吧”

“这 …… 这 ……”,zero 刚刚建立起来的对回避陷阱的自信再度崩塌。

“要回避一切模棱两可的问题是不可能的,”,Solmyr 站起身来,“关键是了解它为什么会发生,怎样的情况容易诱发它,然后小心的加以处理,C++ 中很多问题都是如此。这块《 50 诫》的石板就留给你了,好好研读吧。哈哈哈哈!”,Solmyr 一边笑着一边离开了 zero ,背影看起来像是一位飘然远去的高人 ……

“什么呀!根本就只是一个性格残暴的家伙而已,装模做样 …… 啪!”,zero 话音未落,一个文件夹划破空气飞来,正中 zero 的面门。

“呜 ~ 我什么也没说 ~”,zero 无力的辨白,然而换来的只是旁边的座位上再度传来低低的窃笑声而已。zero 明白,今天他的形象算是彻底的毁了 ……

Solmyr 的小品文系列之二:模棱两可的陷阱相关推荐

  1. (C++的对话)Solmyr 的小品文系列之二:模棱两可的陷阱

    原作者:Solmyr   Form pchome.net "为什么会这样?!",zero 一边喝水一边嘟囔着,恨恨的看着面前显示器上的代码,"为什么这么简单的一个调用也会 ...

  2. 经典算法研究系列:二、Dijkstra 算法初探

    经典算法研究系列:二.Dijkstra 算法初探  July   二零一一年一月 ====================== 本文主要参考:算法导论 第二版.维基百科. 写的不好之处,还望见谅. 本 ...

  3. 容器开启数据服务之旅系列(二):Kubernetes如何助力Spark大数据分析

    摘要: 容器开启数据服务之旅系列(二):Kubernetes如何助力Spark大数据分析 (二):Kubernetes如何助力Spark大数据分析 概述 本文为大家介绍一种容器化的数据服务Spark ...

  4. Skype for business混合部署系列之二自定义拓扑信息

    Skype for business混合部署系列之二自定义拓扑信息 此次部署前端服务器共3台,后端数据库2台采用always on方式,2台SQL Server服务器已经安装完成,在这里不做文档,本章 ...

  5. 黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (高级)

    原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (高级) 本章介绍的是企业库加密应用程序模块 ...

  6. 深入理解 Linux Cgroup 系列(二):玩转 CPU

    原文链接:深入理解 Linux Cgroup 系列(二):玩转 CPU 上篇文章主要介绍了 cgroup 的一些基本概念,包括其在 CentOS 系统中的默认设置和控制工具,并以 CPU 为例阐述 c ...

  7. 【冰极峰教程系列之二】:牢不可破的九宫格布局

    原创:冰极峰 转载请注明出处 时间:2009年6月22日 8:40:16 冰极峰教程系列之一:九宫格基本布局 冰极峰教程系列之二:牢不可破的九宫格布局 冰极峰教程系列之三:三层分离的完美九宫格 冰极峰 ...

  8. WPF技术触屏上的应用系列(二): 嵌入百度地图、API调用及结合本地数据库在地图上进行自定义标点的实现...

    原文:WPF技术触屏上的应用系列(二): 嵌入百度地图.API调用及结合本地数据库在地图上进行自定义标点的实现 去年某客户单位要做个大屏触屏应用,要对档案资源进行展示之用.客户端是Window7操作系 ...

  9. MySQL优化系列(二)--查找优化(1)(非索引设计)

    MySQL优化系列(二)--查找优化(1)(非索引设计) 接下来这篇是查询优化,用户80%的操作基本都在查询,我们有什么理由不去优化他呢??所以这篇博客将会讲解大量的查询优化(索引以及库表结构优化等高 ...

最新文章

  1. rsyslog的配置文件使用方法
  2. 练习2-14 求奇数分之一序列前N项和 (15 分)
  3. Invalid character escape '\o'.
  4. linux下oracle修改最大连接数,linux修改TCP最大连接数
  5. 一站配齐所有国内镜像,第三方库安装提升 10 倍
  6. mock以及特殊场景下对mock数据的处理
  7. code换取微信openid_关于PHP如何获取微信的openID教程
  8. 修改win10我的文档下载等移动别处
  9. 程序阅读:简单C++学生信息管理系统
  10. php webm文件头,webm EBML文件头分析
  11. java 调用 axis2_java调用WebService服务 axis2实现方式
  12. 叶俊—中国的安东尼·罗宾
  13. drupal mysql hash密码_drupal7 密码重置
  14. 成为JAVA架构师必看书籍推荐
  15. 怎样使用轻快pdf阅读器
  16. Selenium使用中的SSL错误
  17. python的MYSQLdb
  18. Excel时间做差,统计加班时长
  19. APP商店货币化的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  20. 实施工程师日常必备技能

热门文章

  1. linux ksh 用户名,linux ksh
  2. 2020年中国丙烯产能产量、进出口及需求现状分析,“十四五”进口缺口将继续缩小「图」
  3. PLSQL Developer安装配置教程(超详细)
  4. LeetCode224 基本计算器
  5. 5分钟商学院-个人篇-逻辑思维
  6. spring报错:ORA-01017: invalid username/password; logon denied
  7. C语言指针之NULL
  8. 清除CMOS方法大全
  9. Mockjs基础用法
  10. 【RPA之家转载】中国银河证券通过 RPA 系统和工具评估,RPA应用成熟度达到国内领先水平