Effective STL Item 6: Be alert for C++’s most vexing parse.
来源:互联网 发布:spu sku 数据库设计 编辑:程序博客网 时间:2024/06/11 20:54
为什么这段 c++ 代码需要加这对括号才正确运行,是编译器问题吗?
std::ifstream fin("test.txt");std::string str((std::istreambuf_iterator<char>(fin)), // 这里多了一对括号 std::istreambuf_iterator<char>());cout << str;
std::ifstream fin("test.txt");std::string str(std::istreambuf_iterator<char>(fin), // 这里少了括号 std::istreambuf_iterator<char>());cout << str;
第二段代码的错误信息:
1>源.obj : error LNK2019: 无法解析的外部符号 "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl str(class std::istreambuf_iterator<char,struct std::char_traits<char> >,class std::istreambuf_iterator<char,struct std::char_traits<char> > (__cdecl*)(void))" (?str@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$istreambuf_iterator@DU?$char_traits@D@std@@@2@P6A?AV32@XZ@Z),该符号在函数 _main 中被引用
1>C:\Code\C++\C++ Primer\Test\Debug\Test.exe : fatal error LNK1120: 1 个无法解析的外部命令
前一段代码可以输出文件里的内容,但是后一段代码出错?
两段代码都没语法错误吧,为什么结果不一样?
假设有个文件里面记录的一系列的 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 above6: 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.
- Item 6. Be alert for C++'s most vexing parse.
- C++'s most vexing parse
- C++'s most vexing parse
- Effective STL 6 Vexing parse
- [C++] Most Vexing Parse
- cpp's most vexing parse
- effective stl item 6
- effective stl读书笔记 -- Item 6
- effective stl读书笔记 —— Item 6
- 《Effective STL》读书笔记四:Item 6 - 9
- <Effective Mordern C++>Item 6:UseTheExplicitlyTypedInitializerIdiomWhenAutoDeducesUndesiredTypes
- effective stl读书笔记 —— Item 19
- effective stl读书笔记 —— Item 1
- effective stl读书笔记 —— Item 2
- effective stl读书笔记 —— Item 3
- effective stl读书笔记 —— Item 4
- effective stl读书笔记 —— Item 5
- 自定义view之超级课程表页面的实现
- Android中高德地图与百度地图坐标转换
- redis+mybatis+spring
- windows坏境下NATS服务搭建与测试
- const与#define
- Effective STL Item 6: Be alert for C++’s most vexing parse.
- 《Spark MLlib 机器学习》第四章至十三章代码
- lightoj 1020 - A Childhood Game 简单博弈
- 小端口驱动启动及关闭过程
- CentOS下Storm 1.0.0集群安装详解
- ibatis遍历数组
- 持续集成之Jenkins插件使用(一)- 多个job之间的串并联
- CentOS 7搭建Grafana环境
- android material design widget recyclerview