免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12
最近访问板块 发新帖
楼主: sonicling
打印 上一主题 下一主题

大湿 Lippman 也犯错? [复制链接]

论坛徽章:
3
寅虎
日期:2013-11-27 07:53:29申猴
日期:2014-09-12 09:24:152015年迎新春徽章
日期:2015-03-04 09:48:31
11 [报告]
发表于 2012-05-15 13:44 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
12 [报告]
发表于 2012-05-15 13:56 |只看该作者
回复 11# Sevk


    区别肯定是有的,共性肯定也是有的。就看怎么去看了。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
13 [报告]
发表于 2012-05-15 14:31 |只看该作者
回复 7# bruceteen

嗯,看到int (*pf)时无法确定。
甚至对int (*pf)( ... ),如果不确定其中的内容,依然无法确定pf是:
1. 函数指针,返回int, ... 是该函数的形式参数
2. 指针,类型int*, ...是构造函数的实际参数



直到确定 ... 的内容了, 比如:
i) 0
那只能解释为指针。

ii) 1024 —— 原始例子
也只能解释为指针。然后1024这一非0整数常量初始化指针再报告一个错误。

iii) 空 —— 原始后续例子
可以解释为两者:
a) 函数指针,形式参数列表为空
b) 指针,实际参数列表为空
于是meta-rule决定是a) 而不是b)。 (假设如果meta-rule决定是b),那编译会成功的,同i),都是一个空int指针。)

iv) 前面3个例子确实是用脚丫子能想出来的
更奇葩的例子,人肉分析很容易中招的,也就是Eff STL里面提到的那种类型:

  1. typedef int* pint; // 为了可读性(或者说迷惑性)取个名字
  2. int (*p0)(pint()); // 可以解释为用 pint() 产生的临时对象(空指针)作为 p0 这个int*变量的构造函数的实际参数

  3. int* q = ...;
  4. int (*p1)(pint(q)); // 可以解释为用 pint(q) 产生的临时对象(与q相同的指针)作为p1这个int*变量的构造函数的实际参数
复制代码
但同时,p0与p1都可以解释为函数指针…… 关键点是:

  1. void f0(pint f()) {} // 通常写法
  2. void f1(pint  ()) {} // 省略参数名,依然可以,这还不算太意外

  3. void g0(pint  x ) {} // 通常的写法
  4. void g1(pint (x)) {} // 多加一对括号,依然可以,囧死
复制代码
所以p0又可以解释为函数指针,返回类型int,形式参数是一个省略名字的函数指针,该函数指针不接受参数并返回pint。
而p1可以解释为函数指针,返回类型int,形式参数名是q类型是pint。

最终根据meta-rule,p0,p1是指针变量(后面的括号是构造函数的实际参数列表)被排除;p0,p1都是函数指针,后面的括号都是形式参数列表。

论坛徽章:
0
14 [报告]
发表于 2012-05-15 15:01 |只看该作者
回复 6# OwnWaterloo


    看了一下Efficient STL里面所说的Most Vexing Parse。我觉得他的例子没解释清楚。

list<int> data(istream_iterator<int>(dataFile), // warning! this doesn't do
istream_iterator<int>()); // what you think it does


他是说这个是声明了一个函数,输入一对istream_iterator作为参数,其中前面那个有名字,后面没名字,并返回list。

istream_iterator<int>(dataFile) 可以看作是参数声明,而不是当作postfix-expression,没问题,因为这种形式本来就具有二义性,怎么解释都不算错。

istream_iterator<int>() 是啥?是省略了参数名的 istream_iterator<int> 参数吗?错!是返回istream_iterator<int>,参数为空的函数类型。

parameter-declaration:
    decl-specifier-seq declarator
    decl-specifier-seq declarator = assignment-expression
    decl-specifier-seq abstract-declaratoropt      <-------------- 重点看这一行
    decl-specifier-seq abstract-declaratoropt = assignment-expression


未命名的parameter,形式上只能是decl-specifier-seq (abstract-declarator部分被省去),后面不带括号。
如果有括号,括号部分应该是abstract-declarator
abstract-declarator:
    ptr-operator abstract-declaratoropt
    direct-abstract-declarator
direct-abstract-declarator:
    direct-abstract-declaratoropt ( parameter-declaration-clause ) cv-qualifier-seqopt exception-specificationopt
    direct-abstract-declaratoropt [ constant-expressionopt ]
    ( abstract-declarator )


如果要把空括号看作是abstract-declarator的话,括号部分应该是  ( parameter-declaration-clause ) ,其中 parameter-declaration-clause 可以接受空,那意义就不仅仅是参数名被省略,他是一个没有名字的函数原型声明!但是函数原型可以做参数类型吗?我没试过,感觉会有问题。


我说这些是为了说明C++的语法的确是非常复杂。Lippman 说的没错,C++的语法解析对编译器来说负担巨大。

int abcd(); // 函数声明 abcd: {} -> int
int abcd(double); // 函数声明 abcd: double -> int
int abcd(double ()); // 函数声明 abcd: double (*)() -> int 姑且把函数原型当作函数指针
int (*abcd)(double()); // 函数指针声明 abcd : pointer to (double (*)() -> int)  姑且把函数原型当作函数指针
int (*abcd)(double(0)); // 指针声明并初始化 int *abcd = double(0) 有类型转换问题
int (*abcd)(double(*)); // 函数指针声明 abcd: pointer to (double * -> int)

论坛徽章:
0
15 [报告]
发表于 2012-05-15 15:05 |只看该作者
GCC、CLang的语法分析器都是手工写的递归下降分析,对不同语法成分的分析分散在不同函数里面。现在的问题是只要输入序列有轻微差别,整个序列的意义可能发生根本性改变。对编译器实现是巨大挑战。

论坛徽章:
3
寅虎
日期:2013-11-27 07:53:29申猴
日期:2014-09-12 09:24:152015年迎新春徽章
日期:2015-03-04 09:48:31
16 [报告]
发表于 2012-05-15 17:23 |只看该作者
提示: 作者被禁止或删除 内容自动屏蔽

论坛徽章:
0
17 [报告]
发表于 2012-05-15 18:52 |只看该作者
那得把所有函数名当作函数指针变量来处理,而不能是隐式转换为函数指针。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP