在Stack Overflow教会我的许多事情中,有一件事被称为“最令人烦恼的解析”,这是一行经典的演示,如
A a(B()); //declares a function
虽然对于大多数人来说,直观上看起来像是A
类型的对象a
的声明,将临时B
对象作为构造函数参数,但它实际上是返回A
的函数a
的声明,将指针指向返回B
的函数,并且本身不带参数。同样,线
A a(); //declares a function
也福尔斯同一类别,因为它声明的不是对象,而是函数。现在,对于第一种情况,通常的解决方法是在B()
周围添加一组额外的方括号/圆括号,因为编译器会将其解释为对象的声明
A a((B())); //declares an object
但是,在第二种情况下,执行相同的操作会导致编译错误
A a(()); //compile error
我的问题是为什么是的,我很清楚正确的“变通方法”是将其更改为A a;
,但我很好奇地想知道第一个示例中额外的()
对编译器有什么作用,然后在第二个示例中重新应用它时就不起作用了。A a((B()));
解决方案是否是标准中写入的特定例外?
5条答案
按热度按时间vpfxa7rd1#
没有开明的答案,这只是因为它没有被 C++ 定义为有效的语法。从语言的定义来看,就是这样。
如果你有一个表达式,那么它是有效的。例如:
更简单的说法是:因为
(x)
是有效的C++表达式,而()
不是。要了解更多关于语言是如何定义的,以及编译器是如何工作的,您应该了解Formal language theory,或者更具体地说,Context Free Grammars (CFG)和相关材料,如有限状态机。如果你对此感兴趣,尽管维基百科上的页面还不够,你还是得买本书。
xj3cbfub2#
这个问题的最终解决方案是如果可能的话,迁移到C+11统一初始化语法。
http://www.stroustrup.com/C++11FAQ.html#uniform-init
vm0i2vca3#
C函数声明符
首先,有C在C中,
A a()
是函数声明。例如,putchar
具有以下声明。通常,这样的声明存储在头文件中,但是如果你知道函数的声明是什么样子的,没有什么能阻止你手工编写它们。参数名称在声明中是可选的,所以我在这个例子中省略了它。这允许你像这样写代码。
C语言还允许你定义以函数为参数的函数,它的语法看起来像一个函数调用(只要你不返回一个指向函数的指针,它就是可读的)。
正如我提到的,C允许在头文件中省略参数名,因此
output_result
在头文件中看起来像这样。构造函数中的一个参数
你不认识这个吗?好吧,让我提醒你。
是的,这是完全相同的函数声明。
A
是int
,a
是output_result
,B
是int
。你可以很容易地注意到C与C新特性的冲突。确切地说,构造函数是类名和括号,并且声明语法是
()
而不是=
。通过设计,C试图与C代码兼容,因此它必须处理这种情况-即使实际上没有人关心。因此,旧的C特性优先于新的C特性。声明的语法尝试将名称匹配为函数,如果失败,则恢复为新语法()
。如果这些特性中的一个不存在,或者有不同的语法(比如C11中的
{}
),那么对于只有一个参数的语法来说,这个问题就永远不会发生。现在你可能会问为什么
A a((B()))
工作。好吧,让我们用无用的括号声明output_result
。没用的语法要求变量不在括号中。
但是,C在这里需要标准表达式。在C中,您可以编写以下代码。
和以下代码。
C++期望括号内的表达式是…表达式,而不是C期望的类型。括号在这里没有任何意义。然而,通过插入无用的括号,C函数声明不匹配,新语法可以正确匹配(只需要一个表达式,如
2 + 2
)。构造函数中的更多参数
一个论点当然很好,但两个呢?这并不是说构造函数只能有一个参数。
std::string
是接受两个参数的内置类之一这一切都很好(从技术上讲,如果它被写为
std::string wat(int(), char())
,它将有最令人烦恼的解析,但让我们诚实地说-谁会写这样的呢?但是让我们假设这段代码有一个令人烦恼的问题。你会认为你必须把所有的东西都放在括号里。不完全是这样。
我不知道为什么g++试图将
char
转换为const char *
。无论哪种方式,构造函数都只使用一个char
类型的值调用。没有重载只有一个char
类型的参数,因此编译器会感到困惑。你可能会问-为什么参数是char类型?是的,这里的
,
是一个逗号运算符。逗号运算符接受两个参数,并给出右侧参数。这不是真的有用,但它是我的解释知道的东西。相反,要解决最令人烦恼的解析,需要以下代码。
参数在括号中,而不是整个表达式。事实上,只要有一个表达式需要放在括号中,因为使用C++功能就足以稍微打破C语法。事情把我们带到了零争论的地步。
构造函数参数清零
您可能已经注意到我的解释中的
eighty_four
函数。是的,这也受到最令人烦恼的解析的影响。这是一个有效的定义,如果您创建了头文件(您应该),您很可能已经看到了它。添加括号并不能解决这个问题。
为什么会这样呢?
()
不是表达式。在C中,必须将表达式放在括号之间。你不能在C中写auto value = ()
,因为()
没有任何意义(即使有,像空元组(见Python),它也是一个参数,而不是零)。实际上,这意味着你不能在不使用C++11的{}
语法的情况下使用速记语法,因为没有表达式可以放在括号中,并且函数声明的C语法将始终适用。4dc9hkyq4#
你可以代替
使用
guz6ccqo5#
在你的例子中,最里面的括号是一个表达式,在C中,语法将
expression
定义为assignment-expression
或另一个expression
,后跟一个逗号和另一个assignment-expression
(附录A.4 -语法摘要/表达式)。语法进一步将
assignment-expression
定义为其他几种类型的表达式之一,其中没有一种可以是nothing(或只有空格)。所以不能使用
A a(())
的原因很简单,因为语法不允许这样做。然而,我不能回答为什么创造C的人不允许这种特殊情况下使用空括号-我猜如果有合理的替代方案,他们宁愿不把这种特殊情况。