Effective STL 笔记: Item 6--Be alert for C++'s most vexing parse
假设有个文件里面记录的一系列的 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);
这种写法应该通用。
(使用许可:署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议 。)
Effective STL 笔记: Item 6--Be alert for C++'s most vexing parse的更多相关文章
- Effective STL 笔记 -- Item 6 ~ 7: Container and Object Pointer
Effective STL 笔记 – Item 6 ~ 7: Container and Object Pointer 中间两次笔记被删掉了,简单补一下: Item 3 中提到如果将对象直接放入容器中 ...
- Effective STL 笔记 -- Item 9: Choose carefully among erasing options
假设有一个容器中存放着 int ,Container<int> c, 现在想从其中删除数值 1963,可以有如下方法: 1: c.erase(remove(c.begin(), c.end ...
- C++学习书籍推荐《Effective STL(英文)》下载
百度云及其他网盘下载地址:点我 作者简介 Scott Meyers is one of the world's foremost authorities on C++, providing train ...
- STL笔记(6)标准库:标准库中的排序算法
STL笔记(6)标准库:标准库中的排序算法 标准库:标准库中的排序算法The Standard Librarian: Sorting in the Standard Library Matthew A ...
- 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 ...
- Effective STL 学习笔记 Item 34: 了解哪些算法希望输入有序数据
Effective STL 学习笔记 Item 34: 了解哪些算法希望输入有序数据 */--> div.org-src-container { font-size: 85%; font-fam ...
- Effective STL 学习笔记 Item 30: 保证目标区间足够大
Effective STL 学习笔记 Item 30: 保证目标区间足够大 */--> div.org-src-container { font-size: 85%; font-family: ...
- 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 ...
- Effective STL 学习笔记: Item 22 ~ 24
Effective STL 学习笔记: Item 22 ~ 24 */--> div.org-src-container { font-size: 85%; font-family: monos ...
随机推荐
- linux内核分析第3章&第18章读书笔记
linux内核分析第3章&第18章读书笔记 第三章 进程管理 进程:处于执行期的程序(目标码存放在某种存储介质上) 包含资源:可执行程序代码,打开的文件,挂起的信号,内核内部数据,处理器状态, ...
- 复习JavaScript随手记
数据类型 基本类型 string number boolean undefined number类型,包含整数浮点数 NaN和自己都不相等,涉及NaN的计算结果都是NaN isNaN()函数用于判断一 ...
- Linux下的tar压缩解压缩命令详解(转)
tar -c: 建立压缩档案-x:解压-t:查看内容-r:向压缩归档文件末尾追加文件-u:更新原压缩包中的文件 这五个是独立的命令,压缩解压都要用到其中一个,可以和别的命令连用但只能用其中一个.下面的 ...
- C++单例模式设计与实现
C++单例模式主要用途就是整个程序中只实例化一个对象,之后获取到的都是该对象本身进行处理问题. 单例模式一般都是在函数中采用局部静态变量完成的,因为局部的静态变量生命周期是随着程序的生命周期 一起结束 ...
- python基础之列表、字典、元祖等 (二)
一.作用域 if 1==1: name = 'weibinf' print name 下面的结论对吗? 外层变量,可以被内层变量使用 内层变量,无法被外层变量使用 二.三元运算 result = 值1 ...
- golang channel状态表
如果我们查看该表,可以察觉到在操作中可能产生问题的地方.这里有三个可能导致阻塞的操作,以及三 个可能导致程序恐慌的操作. 乍看之下,通道的使用上限制很多,但在检查了这个限制产生的动机并熟悉 了通道的使 ...
- .net core 中 identity server 4 之Topic --定义Client
客户端指能够从id4获取Token的角色. 客户端的共性: a unique client ID a secret if needed the allowed interactions with th ...
- 千里之行始于足下,node.js 资源中文导航
响应@jiyinyiyong 号召,cnodjs 好的资源蛮多的,的确欠缺分类,在一群FAQ中,的确很容易沉下去,根据自己对node.js的理解,做成一个资源导航,PS:如果觉得合适,希望能够合并的c ...
- 用Matlab实现字符串分割(split)
用Matlab实现字符串分割(split)Posted on 2011/08/08 Matlab的字符串处理没有C#强大,本身又没有提供OO特性,需要依赖别的手段完成这项任务. 我们在这里借助正则表达 ...
- 微信小程序开发(四)线程架构和开发步骤
线程架构 从前面的章节我们可以知道,.js文件是页面逻辑处理层.我们可以按需在app.js和page.js中添加程序在生命周期的每个阶段相应的事件.如在页面的onLoad时进行数据的下载,onShow ...