Effective STL [6] | 警惕 C++ 最令人恼怒的解析

警告
本文最后更新于 2023-08-26,文中内容可能已过时。
  1. 声明一个函数 f 带有一个 double 而且返回一个 int:
1
int f(double d);
  1. 名为 d 的参数左右的括号是多余的,被忽略:
1
int f(double (d)); // 同上;d左右的括号被忽略
  1. 省略了参数名:
1
int f(double); // 同上;参数名被省略
  1. 第一个声明了一个函数 g,它带有一个参数,那个参数是指向一个没有参数、返回 double 的函数的指针:
1
int g(double (*pf)()); // g带有一个指向函数的指针作为参数
  1. 唯一的不同是 pf 使用非指针语法来声明 (一个在 C 和 C++ 中都有效的语法):
1
int g(double pf()); // 同上;pf其实是一个指针
  1. 照常,参数名可以省略,所以这是 g 的第三种声明,去掉了 pf 这个名字:
1
int g(double ()); // 同上;参数名省略

注意参数名左右的括号(就像 f 的第二种声明中的 d)和单独的括号(正如本例)之间的区别。

参数名左右的括号被忽略,但单独的括号指出存在一个参数列表:它们声明了存在指向函数的指针的参数。

假设有一个 int 的文件,想要把那些 int 拷贝到一个 list 中,可能会使用下面代码:

1
2
3
4
ifstream dataFile("ints.dat");
// 警告!这完成的并不是像你想象的那样
list<int> data(istream_iterator<int>(dataFile),
istream_iterator<int>());

这里的想法是传一对 istream_iteratorlist 的区间构造函数,因此把 int 从文件拷贝到 list 中。

这段代码可以编译,但是运行时什么都不会做,不会从文件中读出任何数据,甚至不会构建 1 个 list

第二句并不声明 list,也不调用构造函数。

这声明了一个函数 data,它的返回类型是 list<int>。这个函数 data 带有两个参数:

● 第 1 个参数叫做 dataFile。它的类型是 istream_iterator<int>。dataFile 左右的括号是多余的而且被忽略。

● 第 2 个参数没有名字。它的类型是指向一个没有参数而且返回 istream_iterator<int> 的函数的指针。

就像下面具有这条规则的代码:

1
2
class Widget {...}; // 假设Widget有默认构造函数
Widget w();

这并没有声明一个叫做 w 的 Widget,它声明了一个叫作 w 的没有参数且返回 Widget 的函数。

本来代码的初衷,是用一个文件的内容来初始化一个 list<int> 对象,现在并没有达到我们的期望。

  1. 函数调用前后增加括号 用括号包围一个实参的声明是不合法的,但用括号包围一个函数调用的观点是合法的,所以通过增加一对括号,代码变为:
1
2
list<int> data((istream_iterator<int>(dataFile)), // 注意在list构造函数的第一个实参左右的新括号
istream_iterator<int>());

这是可能的声明数据方法,给予 istream_iterators 的实用性和区间构造函数。

  1. 命名迭代器对象
1
2
3
4
ifstream dataFile("ints.dat");
istream_iterator<int> dataBegin(dataFile);
istream_iterator<int> dataEnd;
list<int> data(dataBegin, dataEnd);

命名迭代器对象的使用和普通的 STL 编程风格相反,但是你得判断这种方法对编译器和必须使用编译器的人都模棱两可的代码是一个值得付出的代价。

Buy me a coffee~
Jian YE 支付宝支付宝
Jian YE 微信微信
0%