我不理解为什么这个可以编译通过

81

我肯定是遗漏了什么,但我不明白为什么这个代码可以通过编译(无论是使用g++还是clang++):

struct A
{
};
struct B
{
};

int main()
{
  A a(B);
}

首先,B 是一种类型,而不是一个值。我该如何解释这段代码?


38
这被称为“最棘手的解析”(Most Vexing Parse)。 - alter_igel
8
真的吗?在这种情况下,没有歧义。它只能是一个函数声明。它不是 A a(B()); ,后者可能是一个变量定义或函数声明。 - walnut
8
你会惊讶地知道,struct A{}; int main() { A(foo); }即使foo没有指定任何内容,也可以如此编译。 - Ayxan Haqverdili
20
@alterigel——这不是最令人烦恼的解析方式。请看您提供链接页面上的例子。这只是一个函数声明。 - Pete Becker
3
@PeteBecker,最好解释一下为什么这不是MVP,而不仅仅是断言它不是,我相信walnut已经在上面做过了解释。 - JPhi1618
显示剩余3条评论
3个回答

87

其实是一个名为 a 的函数声明,它接受一个类型为 B 的参数并返回一个 A 类型的值。


5
这就是为什么它是最麻烦的。解决方案是:(虽然它并没有真正解决问题,因为它暴露了不良构造)A a{B}; - user4581301
23
@user4581301 -- 这不是最棘手的解析。它只是一个函数声明。 - Pete Becker
11
最奇怪的是,C++不允许嵌套函数,但却允许在函数内进行声明。 - The_Sympathizer
6
听起来这是为 C++ 添加支持嵌套函数的好动机;它们不仅有用,还能将这个奇怪的缺陷转变为一个合理的设计 :) - Jeremy Friesner
3
“有点违反直觉”可能是C++的座右铭 :)。至于前向声明,我认为对于本地函数,它们不被支持,就这样吧。 - Jeremy Friesner
显示剩余5条评论

15

这只是一个函数声明,声明了a为返回A类型、并接受一个B类型未命名参数的函数。

这是合法的,因为在函数定义中允许函数声明而非函数定义。


13

这个问题被称为最具挑战性的解析。行A a(B);可以被解释为声明一个名为a的函数,返回类型为A的对象,并带有一个未命名的类型为B的参数。

避免此问题的一种方法是使用C++11中引入的统一初始化语法,其中使用花括号代替括号:A a{B};会返回错误。现在将该行解释为变量声明并用B进行初始化,而B是一个类型而不是一个值。

这里有更多信息:

最具挑战性的解析:如何快速发现并修复它


12
我认为这不应该被称为“最困惑的解析”。它只是一个普通的函数声明,与C语言中存在的声明方式相同。由于该行只能是一个函数声明,因此不需要消除任何歧义。请看你提供的链接。那些例子都与这个不同。 - walnut
3
虽然这是正确的,但它与最棘手的分析有关。只是这也包括了一个错别字,其中仅使用了一个类型名称,而没有使用变量或构造函数调用,这可能是最初的意图。 - Miral
1
是的,在这种情况下,“最令人烦恼的解析”是一个有用的答案,即使问题中的实际情况只是“稍微令人烦恼的解析”。 - jpa
1
@wlanut:空结构体struct A { };在标准C中是无效的,即使有些编译器允许。去掉大括号就不会有问题了。此外,在C语言中,声明或定义struct A并不会创建类型名A(必须在A被使用时加上struct前缀,或者在A之前添加typedef struct A A;)。同样在C语言中,函数声明没有其他解析方式——使用type name(...);永远不能成为变量定义;它总是一个函数声明(或无效)。问题中的代码在C语言中是无效的。 - Jonathan Leffler
@jpa,我在任何地方都找不到关于“Slightly Vexing Parse”的任何资料。这个名字只是口头传统吗?我想多了解一些。 - Keith Russell
@KeithRussell 这只是一个文字游戏。这个问题是一个更简单的情况,与众所周知的“最令人烦恼的解析”有关。这就是为什么我将这个问题称为“稍微令人烦恼的解析”的原因。 - jpa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接