一个非常烦人的解析错误:没有参数的构造函数

55

我正在使用g++在Cygwin中编译一个C++程序,其中有一个构造函数没有参数,我有以下代码:

MyClass myObj();
myObj.function1();

我试图编译时,出现了以下错误信息:

error: request for member 'function1' in 'myObj', which is of non-class type 'MyClass ()()'

经过一番研究,我发现解决方法是将第一行改为:

MyClass myObj;

我能发誓以前在C++中用括号声明过空构造函数。这可能是我正在使用的编译器的限制吗?还是语言标准确实说不要对没有参数的构造函数使用括号?


关于您的编辑,大多数人不会找到这个问题,因为“vexing”在标题中。这只有助于管理员更好地找到它,也许。我之所以能找到它,是因为我在谷歌上搜索了“error declaring variable with empty parentheses c++”。您介意我们把标题改回原来的样子吗? - NathanOliver
@NathanOliver:我的编辑是为了帮助管理员正确决定哪个错误是重复的 - 因为这不是一个明显的选择。但如果你想要一个更长的版本,其中包含“最令人烦恼的解析”这个短语和更多原始内容,那就去吧。 - einpoklum
7个回答

63
尽管MyClass myObj();可以被解析为一个带有空初始化器的对象定义或函数声明,但语言标准规定,模棱两可情况始终会被解决为函数声明。其他上下文中允许使用空括号初始化器,例如在new表达式中或构造值初始化的临时对象。

58

这被称为最令人沮丧的解析问题。当解析器遇到

MyClass myObj();

它认为你正在声明一个名为myObj的函数,该函数没有参数,并返回一个MyClass

要绕过这个问题,请使用:

MyClass myObj;

除了堆栈分配之外,MyClass objMyClass *obj = new MyClass() 之间是否有任何区别? - SexyBeast
1
第一个声明了objMyClass类型的对象,并且在作用域结束时将自动释放。第二个声明了objMyClass*类型的对象,必须手动释放,并且在作用域结束后仍然可用。 - Peter Alexander
是的,没错,这正是在堆栈和堆上分配内存的结果,对吧? - SexyBeast
@PeterAlexander:如果使用自定义的非默认构造函数,例如:MyClass myObj(int i);,那么也可以。有什么不同吗? - Steven Lee

20

我在C++标准(§8.5.8)中发现了以下内容:

一个初始化器为空括号集,即()的对象应该被值初始化。

[注意:由于语法不允许使用()作为初始化器,

X a ();

这并不是声明类 X 的对象,而是声明一个不带参数并返回 X 的函数。在某些其他初始化上下文中也允许使用 ()(5.3.4、5.2.3、12.6.2)。-结束说明]


11

这是一个比较常见的问题,与编译器无关。基本上,你在声明一个返回类型为MyObj的函数。毫不奇怪地,你无法调用它的构造函数。请参见C++ faq lite以获得更好的解释。


扩展 - 这只是在某些情况下的问题。例如,编写“throw myexceptionclass();”,就不会有混淆。在Pete的上下文中,语言是模棱两可的,但是消除歧义的规则(这也是语言严格来说并不真正模棱两可的原因)选择了一种解释。当然,消除歧义的规则意味着语言并不真正模棱两可 - 但是解析专家仍然这么说,所以我也可以!许多语言中最常见的消除歧义规则是运算符优先级和结合性 - C和C ++更加模棱两可,因此存在一些奇怪的问题。 - user180247

4
MyClass myObj();

这被解析为函数声明。该函数名为myObj,不带参数并返回一个MyClass对象。我从未见过编译器接受这种写法。另一方面,MyClass* myPtr = new MyClass();是可以的,也许这让你感到困惑了?


3
你的这行代码让编译器认为你在声明一个名为myObj、不带参数并返回MyClass类型的函数。这种歧义确实很让人烦恼。

1

标准不要求使用括号。

int* x = new int;

这是合法的语法。

在你的情况下,myclass myobj(); 是一个函数原型。而 myclass myobj; 则是一个变量。


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