Effective STL 条款6 : 当心C++另人迷惑的解析

原创 2004年07月23日 16:14:00

条款6: 当心C++另人迷惑的解析<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

假如你有一个整数构成的文件,需要把它复制到一个list.以下代码看起来是一个很有意义的方法:

 

ifstream dataFile("ints.dat");

list data(istream_iterator(dataFile), // warning! this doesn't do

               istream_iterator());        // what you think it does

 

 

这里的想法是传递一对istream_iteratorlist的区间构造函数(Item5),来将文件中的整数

拷贝到列表中.

 

这段代码能通过编译,但在运行期,它不做任何事情.它不会从文件中读取任何数据.它也不创建列表.

因为第二条语句并没有声明一个list,也没有调用构造函数.它做了什么呢?..它做的事情是如此古怪.

以至我不敢直接告诉你,你不会相信。我只能一点点的解释。你坐好了吗?如果没有你应该找一把椅子。

 

我们从最基础的开始。这一行代码声明了一个函数f,它具有一个double类型的参数,返回一个int类型的值。

 

Int f(double d);

 

下面一行起到相同的作用。参数d两边的括号是多余的,将被忽略。

 

Int f(double (d));      //same as above; parens around d are ignored

 

底下这行代码声明同一个函数。它没有漏掉了参数名。

 

Int f(double);        //same as above; parameter name is omitted.

 

你也许对以上三个声明形式很熟悉。尽管将参数名放在括号内的能力很新鲜。(很久以前它对我来说也行新鲜)

再多看三个函数声明。第一个声明了一个具有函数指针参数的函数。这个函数指针指向一个无参并返回double类型的函数。

 

Int g(double (*pf)());   //g takes a pointer to a function as a parameter

 

这有另一个方法。唯一不同之处是pf使用了非指针语法(在CC++中都有效)

 

int g(double pf());    //same as above; pf is implicitly a pointer;

 

与平时一样,参数名可以省略。因些这是第三种声明g的方法,消去名称pf

 

Int g(double ());     //same as above, parameter name is omitted

 

注意括号在参数名称上(比如f的第二个声明中的d)和只有括号(这个例子中)的情况。在参数名上的括号会被忽略,但只有括号表示存在一个参数列表:它们表示出现在这里的参数是一个指向函数的指针。

fg的声明热身后,可以来看看开头的代码了。再次列在这里:

 

list data(istream_iterator(dataFile), istream_iterator());

 

打起精神来,这行代码声明了一个函数,名为data,其返回类型是list。函数data有两个参数:

第一个参数名为dataFile。它的类型是istream_iterator。在dataFile两侧的括号被忽略。

第二个参数没有名字。它的类型是指向函数的指针,指向的函数类型为无参,返回值类型是istream_iterator

 

吃惊吗?但是它说明了一个C++的通用规则,将尽可能多的东西可以被解析为函数声明。如果你使用过一段时间的C++,你肯定碰到过这条规则的其它宣告形式。你多少次发现过这种错误呢?

class Widget {...}; // assume Widget has a default constructor

Widget w();      //'uh oh...

这并未声明一个名为wWidget,它声明了一个名为w无参函数,其返回值为一个Widget。学习并认识这一点就像通过了真正C++程序员的仪式。

这些都很有趣(在它曲折的形式上),但是它不能帮助我们声明我们希望得到的东西:定义一个list对象由文件的内容初始化。现在我们知道如何防止它被解析,这很容易表示。C++不许将形参声明放在括号之内,但函数调用的参数却可以带上括号。因些加上一对括号可以强制编译器用我们的方式读代码。

list data((istream_iterator(dataFile)),  // note new parens

istream_iterator0);                      // around first argument

// to list's constructor

这种形式声明data,使用istream_iterator工具定义一组区间,使用区间构造函数。(再一次,参见条款5),它很值得这样做。

不幸,并不是所有编译器知道这一点。我测试过一些编译器,有一半拒绝data的声明(正确形式),除非使用没有附加括号的不正确形式。

(原文在此:Of the several I tested, almost half refused to accept data's declaration unless it was incorrectly declared without the additional parentheses!

为了安抚这些编译器,你只有闭上眼睛,使用我痛苦的说明为错误的声明方式。但是这是不可移植的,同时也是短视的。最后,现在解析错误的编译器未来总会更正,对吗?(当然)

最好的解决方案是在data声明中不使用流行的匿名istream_iterator对象,简单地给出这些迭代器的名字。以下代码在任何地方都可以工作:

ifstream dataFile(" ints.dat"};

istream_iterator dataBegin(dataFile);

istream_iterator dataEnd;

list data(dataBegin. dataEnd);

这段代码与通用的STL风格不同,它使用了命名的迭代器对象,但是你需要决定使用对于编译器和程序员看来都有岐意的代码是否值得。

Effective C++ 条款9

绝不在构造和析构过程中调用virtual函数本节有个核心的知识点就是在构造函数和析构函数中,virtual函数失去多态性。 试想一下,假设此时在构造函数和析构函数中,virtual函数没...
  • u011058765
  • u011058765
  • 2015-06-22 11:15:54
  • 576

《Effective C++》:条款44-条款45

条款44将与参数无关的代码抽离templates 条款45运用成员函数模板接受所有兼容类型...
  • KangRoger
  • KangRoger
  • 2015-03-12 22:01:36
  • 1566

条款6: 当心C++另人迷惑的解析

 条款6: 当心C++另人迷惑的解析 假如你有一个整数构成的文件,需要把它复制到一个list中.以下代码看起来是一个很有意义的方法: ifstream dataFile("ints.dat");lis...
  • Black_Man
  • Black_Man
  • 2006-07-08 18:59:00
  • 744

《Effective STL》条款3-条款4

条款3使容器里对象的拷贝操作轻量而正确 条款4用empty来代替检查size是否为0
  • KangRoger
  • KangRoger
  • 2015-10-08 23:31:54
  • 948

Effective STL 中文版(完整版)

Winter总算找到《Effective STL》的完整中文版了,奉献给大家。书中作者解释了怎样结合STL组件来在库的设计得到最大的好处。这样的信息允许你对简单、直接的问题开发简单、直接的解决方案,也...
  • sdnxiaotao
  • sdnxiaotao
  • 2008-07-05 13:23:00
  • 501

More Effective C++ 条款6

条款6:自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别很久以前(八十年代),没有办法区分++和--操作符的前缀与后缀调用。这个问题遭到程序员的报怨,于是C++语言...
  • zhc
  • zhc
  • 2001-09-27 23:04:00
  • 710

《Effective C++》学习笔记——条款24

《Effective C++》学习笔记——条款24:若所有参数皆需类型转换,请为此采用non-member函数...
  • lx417147512
  • lx417147512
  • 2014-12-26 23:38:29
  • 939

《Effective C++》学习笔记——条款31

《Effective C++》学习笔记——条款31:将文件间的编译依存关系降至最低
  • lx417147512
  • lx417147512
  • 2015-06-15 13:51:45
  • 1459

Effective C++ 条款25

考虑写出一个不抛出异常的swap函数本节讲解如何自定义一个高效的swap函数对于std名空间中的swap缺省函数如下所示namespace std{ template void swa...
  • u011058765
  • u011058765
  • 2015-06-27 20:34:56
  • 642

《Effective C++》构造/析构/赋值 函数:条款5-条款9

每一个类中都有构造函数、析构函数、赋值操作符。这几个函数是一个类最根本的函数,它控制着创建对象并初始化、对象消亡时的清理以及摆脱旧值赋新值。这样函数如果有问题,那么影响极为严重。 条款5-条款9分别讲...
  • KangRoger
  • KangRoger
  • 2015-01-05 21:23:49
  • 1759
收藏助手
不良信息举报
您举报文章:Effective STL 条款6 : 当心C++另人迷惑的解析
举报原因:
原因补充:

(最多只允许输入30个字)