最令人烦恼的解析的目的是什么?

What is the purpose of the Most Vexing Parse?

提问人:template boy 提问时间:12/29/2012 最后编辑:Fabio says Reinstate Monicatemplate boy 更新时间:11/25/2021 访问量:8430

问:

维基百科上,我发现了这个:

A a( A() );

[这]可以消除歧义,因为

  1. 类 [] 的变量定义,采用类 [] 的匿名实例,或者AA
  1. 函数的函数声明,该函数返回 [] 类型的对象并接受单个(未命名)参数,该参数是返回类型 [] 的函数(并且不接受任何输入)。AA

大多数程序员期望第一个,但C++标准要求将其解释为第二个。

但是为什么?如果大多数 C++ 社区都期望前一种行为,为什么不将其作为标准呢?此外,如果不考虑解析歧义,上述语法是一致的。

有人可以开导我吗?为什么标准将此作为一项要求?

C++ 最烦恼的解析

评论

1赞 Lightness Races in Orbit 12/29/2012
哪里有函数指针?
0赞 template boy 12/29/2012
阅读错误消息 -- ideone.com/12sT80#view_edit_box 函数指针不是类型吗?T (*)()
0赞 Lightness Races in Orbit 12/29/2012
@templateboy:该消息具有误导性:在该上下文中,基本上已经衰减为用于表达式的函数指针,但声明本身与函数指针无关。aa.fa
1赞 template boy 12/29/2012
@downvoter 我可以解释一下吗?

答:

1赞 Lightness Races in Orbit 12/29/2012 #1

没有特别的原因,除了[可能]K-ballo确定的情况。

这只是遗产。已经有了构造形式,所以当没有 ctor args 在起作用时,它似乎从来都不是需要的。int x;T x;

事后看来,我想如果今天从头开始设计语言,那么 MVP 就不会存在......以及大量其他C++怪异之处。

回想一下,C++经过了几十年的发展,即使是现在,也只是由委员会设计的(另见:骆驼)。

6赞 K-ballo 12/29/2012 #2

这只是一个猜测,但这可能是因为使用给定的方法,您可以获得两种行为:

A a( A() ); // this is a function declaration
A a( (A()) ); // this is a variable definition

如果将其行为更改为变量定义,则函数声明将要复杂得多。

typedef A subfunction_type();

A a( A() ); // this would be a variable declaration
A a( subfunction_type ); // this would be a function declaration??

评论

0赞 gluk47 4/3/2016
不太正确,是一个函数声明。它可以改写为 ,即一个函数,该函数命名并返回一个类型的对象,其参数是函数:。尝试编译它以进行最简单的检查)A a( A() );A a (A (*)())aAvoid -> A
6赞 Martin York 12/29/2012 #3

这是递归定义的语法的副作用。

它不是故意这样设计的。它被发现并记录为最令人烦恼的解析。

评论

1赞 Tanveer Badar 7/17/2020
你能详细说明一下吗?
1赞 Martin York 7/17/2020
@TanveerBadar 他们没有故意设计语言,它应该是合乎逻辑的。尽管由于C++的复杂性,有些东西通过了原始设计者,我们现在被它们困住了。“最令人烦恼的解析”只是该语言使用一段时间后发现的常见问题的名称。我们无法破坏向后兼容性,因此我们无法修复语言(不是直接修复,尽管我们已经添加到语言中以减少这个问题)。
26赞 Luchian Grigore 12/29/2012 #4

假设 MVP 不存在。

您将如何声明函数?

A foo();

将是一个变量定义,而不是一个方法声明。你会引入一个新关键词吗?你会有一个更笨拙的函数声明语法吗?或者你宁愿拥有

A foo;

定义一个变量和

A foo();

声明一个函数?

您稍微复杂一些的示例只是为了与这个基本示例保持一致。说“所有可以解释为声明的东西,都将被解释为声明”比“所有可以解释为声明的东西,都将被解释为声明,除非它是一个单一的变量定义,在这种情况下,它是一个变量定义”更容易。

不过,这可能不是它背后的动机,而是一件好事。

17赞 Jerry Coffin 12/29/2012 #5

对于 C++,这很简单:因为规则是在 C 中以这种方式制定的。

在 C 语言中,歧义只出现在 a 和一些相当晦涩的代码中。几乎没有人偶然触发它——事实上,它可能被认为是罕见的,除非是专门为演示这种可能性而设计的代码。然而,无论好坏,这种模棱两可的可能性意味着必须有人解决它——如果没记错的话,它是由丹尼斯·里奇(Dennis Ritchie)解决的,他下令任何可以被解释为声明的东西都将是声明,即使也有一个模棱两可的解释作为定义。typedef

C++ 添加了使用括号进行初始化以及函数调用作为分组的功能,这将歧义从晦涩变为常见。然而,改变它需要打破来自C的规则,正如大多数人所期望的那样解决这个特殊的歧义,而不创建六个更令人惊讶的歧义,除非你愿意完全放弃与C的兼容性。

评论

0赞 Aleksei Petrenko 2/2/2021
我刚刚为一个本科生C++班做办公时间,其中一个学生在他的作业:)
1赞 Brian Bi 11/16/2021
你如何获得 C 语言中的歧义?即使使用 typedef,通常的 C++ 示例也没有 C 类似物,因为 C 没有函数样式强制转换表示法。
0赞 Jerry Coffin 11/17/2021
@BrianBi:pdos.csail.mit.edu/archive/l/c/roskind.html 对情况有很好的阐述。
0赞 Brian Bi 11/17/2021
一旦考虑到先前声明的范围,这些例子实际上并不模棱两可;例如,你只需要知道在该点可见的声明是否是 typedef-name 的。但是,即使你知道某物是否是一种类型,C++令人烦恼的解析仍然是模棱两可的。(1/2)T(*b)[4];T
1赞 Jerry Coffin 11/23/2021
@BrianBi:经过一番思考,(并查看)我终于找到了实际的规则(§6.7.5.3/11):“在参数声明中,括号中的单个 typedef 名称被视为一个抽象声明符,它使用单个参数指定函数,而不是作为声明符标识符周围的冗余括号。我不记得允许它的情况,但它是关于一个被冗余 paren 包围的 typedef 名称。哦,至少在 C99 之前,这仍然存在。我不确定这是唯一的实例,但无论如何,它是一个。
2赞 Brian Bi 11/25/2021 #6

考虑一下程序是否是这样的:

typedef struct A { int m; } A;
int main() { A a( A() ); }

这将是有效的 C,并且 C 的语法只允许一种可能的解释:被声明为函数。C 只允许初始化使用(而不是括号),不允许被解释为表达式。(函数样式强制转换是 C++ 独有的功能。这不是 C 语言中的“令人烦恼的解析”。a=A()

正如维基百科指出的那样,C++的语法使这个例子模棱两可。但是,如果你想让C++赋予这个程序与C相同的含义,那么,很明显,C++编译器将不得不像C编译器一样解释为一个函数。当然,C++可以改变这个程序的含义,使变量的定义 .然而,只有在有充分理由的情况下,才会在 C++ 中引入与 C 的不兼容,我想 Stroustrup 特别希望避免像这样潜在的静默破坏,因为它们会给迁移到 C++ 的 C 用户带来极大的挫败感。aaA

因此,C++ 也将其解释为函数声明,而不是变量定义;更一般地说,采用了这样的规则,即如果看起来像函数样式转换的东西可以在其句法上下文中被解释为声明,那么它应该是。这消除了在所有令人烦恼的解析情况下与 C 不兼容的可能性,确保不采用 C 中不可用的解释(涉及函数样式强制转换的解释)。

Cfront 2.0 Selected Readings(第 1-42 页)提到了表达式声明歧义情况下的 C 兼容性问题,这是最令人烦恼的解析类型。