假设有个文件里面记录的一系列的 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. 20135239益西拉姆 Linux内核分析 操作系统是怎样工作的?

    益西拉姆+ 原创作品+ <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 堆栈 堆栈是C语言程序运行时 ...

  2. c++ 智能指针(转)

    智能指针的使用 智能指针是在 <memory> 标头文件中的 std 命名空间中定义的. 它们对 RAII 或“获取资源即初始化”编程惯用法至关重要. 此习惯用法的主要目的是确保资源获取与 ...

  3. Oracle 解决【ORA-01704:字符串文字太长】(转)

    错误提示:oracle在toad中执行一段sql语句时,出现错误‘ORA-01704:字符串文字太长’.如下图: 原因:一般为包含有对CLOB字段的数据操作.如果CLOB字段的内容非常大的时候,会导致 ...

  4. Python中scatter()函数--转载

    原博地址:http://blog.csdn.net/anneqiqi/article/details/64125186 最近开始学习Python编程,遇到scatter函数,感觉里面的参数不知道什么意 ...

  5. hdu 2608 (数论)

    hdu2608  0 or 1 题意:给你一个数N(N < 2^31), 问从 1--N 所有数的因子和S(N),求 S(N)%2 的值. 链接:http://acm.hdu.edu.cn/sh ...

  6. 「Django」rest_framework学习系列-路由

    自动生成4个url路由:from rest_framework import routersrouter = routers.DefaultRouter()router.register(r'wrx' ...

  7. 前端PHP入门-017-系统内置函数-会查阅API

    大家要知道未来这就是你的工具 如果代码能赚钱,帮你赚钱最多的是基本语法 如果你还在当程序员,你最好的伙伴就是手册 问问你自己:你有CSS2.0/3.0手册,JavaScript手册,Jquery手册, ...

  8. SQL SERVER2008不在同一局域网内实现订阅发布的方法

    我们知道,在新建订阅的时候,必须要使用实际服务器的真实名称才可以,IP地址,别名都不可以,在同一个局域网内是没有问题的.但是两台服务器不在同一个局域网内就不行了 要想实现订阅和发布,本人用到的最简单的 ...

  9. kle 日志收集系统维护之清理索引及索引优化脚本

    logstash每天往es建好索引,按天生成,就目前的需求,需要清理不需要的数据,以保证最新日志的速度展示,哈哈,瞎搞了这个脚本,路过的大神批评. #!/usr/bin/env python # co ...

  10. 搭建Elasticsearch5.6.8 分布式集群

    集群搭建 1.master[192.168.101.175] 配置elasticsearch.yml #集群名称 所有节点要相同 cluster.name: my-application #本节点名称 ...