假设我们有一个存有整数的文件,并试图把它们复制到一个list中,我们也许会写下1
2fstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile), istream_iterator<int>());
这里的想法是传一对istream_iterator给list的区间构造函数,尽管它不会发生编译错误(还不如发生),但运行效果和期望的相差很大。准确的说,它并没有声明一个list,也没有构造。
我们先从简单的部分开始解释,在c++中,以下三种声明均是合法且等价的:1
2
3int f(double d);
int f(double (d));
int f(double);
接着看三个函数声明:1
2
3
4
5
6//g是一个返回值为int的函数,其形参是一个函数指针,该指针指向一个不需要形参且返回值为double的函数
int g(double (*pf)());
//同上,pf其实是一个函数指针
int g(double pf());
//同上,省略了参数名
int g(double ());
请注意围绕参数名的括号,比如第一组第二个的(d)与独立的括号的区别。围绕参数名的括号可以被忽略,而独立的括号则表明参数列表的存在,它说明前面存在一个函数指针参数。
返回到问题,我们仔细观察这一行语句:1
list<int> data(istream_iterator<int>(dataFile), istream_iterator<int>());
这其实是声明了一个函数,其返回值是list<int>,函数名为data,接受两个参数:
第一个参数名为dataFile,是一个迭代器。(认为dataFile两侧括号多余)
第二个参数没有名称,它的类型是指向一个没有参数而且返回istream_iterator的函数的指针
顺便一提,1
2class Widget {...}; // 假设Widget有默认构造函数
Widget w();
这种东西自然是不能构造对象的,因为它其实是声明了一个不接受参数且返回一个Widget的函数。
为了解决我们遇到的这种烦人的分析机制,我们可以给迭代器名字:1
2
3
4ifstream dataFile("ints.dat");
istream_iterator<int> dataBegin(dataFile);
istream_iterator<int> dataEnd;
list<int> data(dataBegin, dataEnd);
尽管这种命名式实参的风格与STL相违背,但是为了避免二义性并增加可读性,这不失为一种优雅的解决方式。