在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()));
变通方法是写入标准的特定异常吗?
发布于 2009-09-15 00:24:32
没有明智的答案,只是因为它没有被C++语言定义为有效的语法……根据语言的定义,是这样的。
如果你在里面有一个表达式,那么它是有效的。例如:
((0));//compiles
更简单的put:因为(x)
是一个有效的C++表达式,而()
不是。
要进一步了解语言是如何定义的,以及编译器是如何工作的,您应该了解Formal language theory,或者更具体地说,了解Context Free Grammars (CFG)和有限状态机等相关内容。如果你对此感兴趣,尽管维基百科的页面还不够,你必须买一本书。
发布于 2011-07-22 01:35:53
发布于 2014-05-19 03:53:51
C函数声明符
首先是C。在C中,A a()
是函数声明。例如,putchar
具有以下声明。通常,这样的声明存储在头文件中,但是,如果您知道函数的声明是什么样子,就不会阻止您手动编写它们。参数名在声明中是可选的,所以我在本例中省略了它。
int putchar(int);
这使您可以像这样编写代码。
int puts(const char *);
int main() {
puts("Hello, world!");
}
C还允许您定义以函数为参数的函数,具有良好的可读性语法,看起来像函数调用(好吧,它是可读性的,只要您不返回指向函数的指针)。
#include <stdio.h>
int eighty_four() {
return 84;
}
int output_result(int callback()) {
printf("Returned: %d\n", callback());
return 0;
}
int main() {
return output_result(eighty_four);
}
正如我所提到的,C允许在头文件中省略参数名,因此头文件中的output_result
将如下所示。
int output_result(int());
构造函数中的一个参数
你认不出那个了吗?好吧,让我提醒你一下。
A a(B());
是的,这是完全相同的函数声明。A
是int
,a
是output_result
,B
是int
。
您可以很容易地注意到C语言与C++的新特性之间的冲突。确切地说,构造函数是类名和圆括号,并用()
代替=
替代声明语法。通过设计,C++试图与C代码兼容,因此它必须处理这种情况-即使实际上没有人关心。因此,旧的C特性优先于新的C++特性。声明的语法尝试与函数的名称匹配,如果失败,则在使用()
恢复到新语法之前。
如果其中一个特性不存在,或者有不同的语法(比如C++11中的{}
),那么这个问题就不会发生在只有一个参数的语法上。
现在你可能会问为什么A a((B()))
会工作。好吧,让我们用无用的括号声明output_result
。
int output_result((int()));
恐怕行不通。语法要求变量不能放在括号中。
<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token
但是,C++在这里需要标准表达式。在C++中,您可以编写以下代码。
int value = int();
和下面的代码。
int value = ((((int()))));
C++要求括号内的表达式为...好吧..。表达式,而不是C所期望的类型。括号在这里没有任何意义。但是,通过插入无用的圆括号,C函数声明将不匹配,并且新语法可以正确匹配(它只需要一个表达式,如2 + 2
)。
构造函数中有更多参数
当然,一个论点是好的,但两个呢?这并不是说构造函数可能只有一个参数。带有两个参数的内置类之一是std::string
std::string hundred_dots(100, '.');
这一切都很好(从技术上讲,如果它被写成std::string wat(int(), char())
,它将有最令人烦恼的解析,但老实说-谁会写呢?但是让我们假设这段代码有一个令人烦恼的问题。你会假设你必须把所有的东西都放在括号里。
std::string hundred_dots((100, '.'));
事实并非如此。
<stdin>:2:36: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
In file included from /usr/include/c++/4.8/string:53:0,
from <stdin>:1:
/usr/include/c++/4.8/bits/basic_string.tcc:212:5: error: initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
basic_string<_CharT, _Traits, _Alloc>::
^
我不确定为什么g++试图将char
转换为const char *
。无论哪种方式,构造函数都是用一个char
类型的值调用的。没有只有一个char
类型参数的重载,因此编译器会感到困惑。你可能会问--为什么参数是char类型?
(100, '.')
是的,这里的,
是一个逗号操作符。逗号操作符接受两个参数,并给出右边的参数。这并不是很有用,但我的解释是值得大家知道的。
相反,为了解决最麻烦的解析,需要以下代码。
std::string hundred_dots((100), ('.'));
参数在括号中,而不是整个表达式。实际上,只需要将其中一个表达式放在括号中,因为使用C++特性稍微脱离了C语法就足够了。事情把我们带到了零争论的地步。
构造函数中的零参数
您可能已经注意到我的解释中的eighty_four
函数。
int eighty_four();
是的,这也会受到最烦人的解析的影响。这是一个有效的定义,如果您创建了头文件,您很可能会看到这个定义(您应该这样做)。添加括号并不能解决这个问题。
int eighty_four(());
为什么会这样呢?好吧,()
不是一个表达式。在C++中,您必须将表达式放在括号之间。您不能用C++编写auto value = ()
,因为()
没有任何意义(即使有,就像空元组一样(请参阅Python),它将是一个参数,而不是零)。实际上,这意味着如果不使用C++11的{}
语法,就不能使用简写语法,因为没有要放在括号中的表达式,并且函数声明的C语法将始终适用。
https://stackoverflow.com/questions/1424510
复制相似问题