假设有个文件里面记录的一系列的 int 值,现在我们想把这些数值存到一个 List 里面,结合 Item 5, 我们可能会写出下面的代码:

ifstream dataFile("ints.data");
list<int> data(istream_iterator<int>(dataFile), // Start of iterator
istream_iterator()); // End of iterator

这段代码可以编译,但运行时并不工作,它不会去调用 list 的构造函数,从而不会生成我们想要的这个 List。

问题,出在 C++ 对代码的解析上。

假设我们需要声明一个函数,该函数接受 double 类型参数并返回 int 类型,C++ 里面,下面三种方法是等效的:

1: int f(double d);  // Old C style.
2: int f(double(d)); // Function style casts.
3: int f(double); // Same as first but skip parameter.

如果我们要声明另外一个函数,该函数同样返回 int,但接受的参数是一个无参数但返回 double 的函数的指针,则下面的声明是等效的:

4: int g(double (*pf)()); // g takes a pinter to a function as paramter.
5: int g(double pf()); // same as above
6: int g(double ()); // same as above, but parameter (function name) skipped.

观察 1 ~ 6 我们可以看到“ 括号”在不同位置时候的不同作用:

  • 参数 周围的括号可以被忽略
  • 单独 的括号实际上意味着这是一个函数指针的参数列表!

了解了这个区别之后再返回来看最开始的那个声明:

list<int> data(istream_iterator<int>(dataFile), istream_iterator<int>());

这个声明定义了一个返回 list<int> 的函数,该函数接受两个参数:

  • 第一个参数名为 dataFile,类型为 istream_iterator<int>,dataFile 两遍的括号可以忽略。
  • 第二个参数是一个函数指针,该函数不接受参数但返回一个 istream_iterator<int>。

这个和我们最开始想象的完全不一样,而产生分歧的原因就在于 C++ 对代码的解析上:

只要表达式可以被解析成 函数 ,那么该表达式 就会被编译器解析成函数 !

想象一下下面这段代码,相信很多人都写出来过,但是它能编译么?

 7: class Widget
8: {
9: public:
10: Widget(){}
11: virtual ~Widget(){}
12: void Show(){}
13: };
14:
15: Widget w();
16: w.Show();

上面片段的第 8 行实际上不是声明了一个 Widget 对象,而是声明了一个用来返回 Widget 对象的函数,第 9 行自然也就出错了。

理解了上面的内容,也就可以想想怎么解决开始时提出的问题了:给形参声明加上括号不合法,但给函数调用的实参加括号是合法的,通过适当的添加括号,问题得以解决:

1: list<int> data((istream_iterator<int>(dataFile)),
2: istream_iterator<int>());

这里第一个参数周围添加了多余的括号,假设编译器仍然认为 data() 是一个函数声明,则第一个形参周围就被添加了括号,这是一个非法的行为,所以编译器会丢掉这种可能,转而匹配下一种可能得匹配,认为该表达式声明了一个 list<data> 的变量,并调用适当的区间函数(Item 5)来进行初始化。

但并非所有的编译器都支持这种匿名对象,如果编译器不支持,我们需要下面的这种显示的写法:

1: ifstream dataFile("ints.dat");
2: istream_iterator<int> dataBegin(dataFile);
3: istream_iterator<int> dataEnd;
4: list<int> data(dataBegin, dataEnd);

这种写法应该通用。

Effective STL 笔记: Item 6--Be alert for C++'s most vexing parse的更多相关文章

  1. Effective STL 笔记 -- Item 6 ~ 7: Container and Object Pointer

    Effective STL 笔记 – Item 6 ~ 7: Container and Object Pointer 中间两次笔记被删掉了,简单补一下: Item 3 中提到如果将对象直接放入容器中 ...

  2. Effective STL 笔记 -- Item 9: Choose carefully among erasing options

    假设有一个容器中存放着 int ,Container<int> c, 现在想从其中删除数值 1963,可以有如下方法: 1: c.erase(remove(c.begin(), c.end ...

  3. C++学习书籍推荐《Effective STL(英文)》下载

    百度云及其他网盘下载地址:点我 作者简介 Scott Meyers is one of the world's foremost authorities on C++, providing train ...

  4. STL笔记(6)标准库:标准库中的排序算法

    STL笔记(6)标准库:标准库中的排序算法 标准库:标准库中的排序算法The Standard Librarian: Sorting in the Standard Library Matthew A ...

  5. Effective STL 学习笔记 Item 38 : Design functor classes for pass-by-value

    Effective STL 学习笔记 Item 38 : Design functor classes for pass-by-value */--> div.org-src-container ...

  6. Effective STL 学习笔记 Item 34: 了解哪些算法希望输入有序数据

    Effective STL 学习笔记 Item 34: 了解哪些算法希望输入有序数据 */--> div.org-src-container { font-size: 85%; font-fam ...

  7. Effective STL 学习笔记 Item 30: 保证目标区间足够大

    Effective STL 学习笔记 Item 30: 保证目标区间足够大 */--> div.org-src-container { font-size: 85%; font-family: ...

  8. Effective STL 学习笔记 Item 26: Prefer Iterator to reverse_iterator and const_rever_itertor

    Effective STL 学习笔记 Item 26: Prefer Iterator to reverse_iterator and const_rever_itertor */--> div ...

  9. Effective STL 学习笔记: Item 22 ~ 24

    Effective STL 学习笔记: Item 22 ~ 24 */--> div.org-src-container { font-size: 85%; font-family: monos ...

随机推荐

  1. (转)MS14-068域内提权漏洞总结

    0x01 漏洞起源 说到ms14-068,不得不说silver ticket,也就是银票.银票是一张tgs,也就是一张服务票据.服务票据是客户端直接发送给服务器,并请求服务资源的.如果服务器没有向域控 ...

  2. Yarn 模式 与 电影受众分析系统

    yarn模式分为两种模式: 一.Yarn-cluster模式 1.通过spark-submit提交spark jar包(Application),与RM进行通信请求启动AM 2.RM接收到请求之后,会 ...

  3. JAVA导出Excel(支持多sheet)

    一.批量导出: /** * * @Title: expExcel * @Description: 批量导出客户信息 * @param @param params * @param @param req ...

  4. 美化mfc界面,给mfc界面加上皮肤

    注明:里面使用到的资源文件在自己的腾讯微云有. 原图: 添加皮肤后: 通过对比就能知道,加上皮肤后给人的感觉就是耳目一新了. 技术详细说明: 这里用到的是一个轻量型的美化工具SkinSharp又称Sk ...

  5. C++11中对容器的各种循环遍历的效率比较

    #include "CycleTimeTst.h" #include <string> #include <vector> #include <lis ...

  6. lsof显示打开的文件

    lsof `which httpd` //那个进程在使用apache的可执行文件 lsof /etc/passwd //那个进程在占用/etc/passwd lsof /dev/hda6 //那个进程 ...

  7. python学习(八)定制类和枚举

    `python`定制类主要是实现特定功能,通过在类中定义特定的函数完成特定的功能. class Student(object): def __init__(self, name): self.name ...

  8. C++ 局部静态变量,全局变量,全局静态变量,局部变量的区别和联系

    C++变量根据定义位置的不同,具有不同的作用域,作用域可分为6种:全局作用域,局部作用域,语句作用域,类作用域,命名作用域和文件作用域. 从作用域看: 全局变量具有全局作用域.全局变量只需在一个源文件 ...

  9. Laravel是怎么实现autoload的?

    用了一阵Laravel后发现很少有include和require,觉得有点奇怪,思考Laravel是怎么完成文件导入的. 其实Laravel依旧还是用include或者require的,只是都写在一个 ...

  10. 那些相见恨晚的 JavaScript 技巧

    JavaScript 的成功让人津津乐道,为 Web 网页编写 JavaScript 代码已经是所有 Web 设计师的基本功,这门有趣的语言蕴藏着许多不为人熟知的东西,即使多年的 JavaScript ...