为什么不A a(()); 工作?
在Stack Overflow教给我的很多东西中,被称为“最令人头痛的parsing”的东西,
A a(B()); //declares a function
虽然这对大多数人来说,直观地看来是A
types的对象的声明,将一个临时的B
对象作为构造函数参数,但实际上是一个函数声明,返回一个A
,并将指针指向返回B
和它本身没有参数。 同样的线
A a(); //declares a function
也属于同一类,因为它不是一个对象,而是声明了一个函数。 现在,在第一种情况下,这个问题通常的解决方法是在B()
周围添加一组额外的括号/括号,因为编译器会将其解释为对象的声明
A a((B())); //declares an object
但是,在第二种情况下,这样做会导致编译错误
A a(()); //compile error
我的问题是,为什么? 是的,我非常清楚正确的“解决方法”是将其更改为A a;
,但是我很想知道第一个例子中的extra ()
对编译器做了什么,然后在第二个例子中重新应用它的时候不起作用。 A a((B()));
解决方法写入标准的特定exception?
没有开明的答案,这只是因为它没有被C ++语言定义为有效的语法……所以根据语言的定义是这样的。
如果你有一个expression式,那么它是有效的。 例如:
((0));//compiles
要了解更多关于如何定义语言以及编译器如何工作的知识,您应该了解forms语言理论或更具体的上下文无关语法(CFG)以及有限状态机等相关材料。 如果你对此感兴趣,虽然维基百科的页面不够用,但你必须得到一本书。
如果可以的话,这个问题的最终解决scheme是转移到C + 11统一初始化语法。
A a{};
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 ++的新特性之间的冲突。 确切地说,构造函数是类名和括号,用()
而不是=
来替代声明语法。 通过devise,C ++试图与C代码兼容,因此它必须处理这种情况 – 即使几乎没有人关心。 因此,旧的Cfunction优先于新的C ++function。 声明的语法试图匹配名称作为函数,然后在()
返回新的语法之前,如果失败。
如果这些特性中的一个不存在,或者具有不同的语法(如C ++ 11中的{}
),那么对于带有一个参数的语法,这个问题就不会发生。
现在你可能会问为什么A a((B()))
有效。 那么,让我们用无用的括号声明output_result
。
int output_result((int()));
它不会工作。 语法要求variables不要放在括号内。
<stdin>:1:19: error: expected declaration specifiers or '...' before '(' token
但是,C ++在这里需要标准expression式。 在C ++中,你可以编写下面的代码。
int value = int();
和下面的代码。
int value = ((((int()))));
C ++期望内部括号内的expression式是…呃…expression式,而不是typesC期望的。 圆括号在这里没有任何意义。 但是,通过插入无用的括号,C函数声明不匹配,并且可以正确地匹配新的语法(简单地期望expression式,例如2 + 2
)。
更多的构造函数参数
当然有一个说法很好,但是两个呢? 这并不是说构造函数可能只有一个参数。 其中一个带有两个参数的内置类是std::string
std::string hundred_dots(100, '.');
这一切都很好(从技术上讲,如果它会被写成std::string wat(int(), char())
,那么最令人烦恼的parsing是,但让我们诚实 – 谁来写呢?但是让我们假设代码有一个令人烦恼的问题,你会认为你必须把所有的东西放在括号里。
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
types的值来调用构造函数。 没有重载有char
types的一个参数,所以编译器很困惑。 你可能会问 – 为什么这个参数是chartypes的?
(100, '.')
是的,这是一个逗号运算符。 逗号运算符有两个参数,并给出右边的参数。 这不是真的有用,但是我的解释是有用的。
相反,要解决最令人头痛的parsing,需要下面的代码。
std::string hundred_dots((100), ('.'));
参数在括号中,而不是整个expression式。 事实上,只有一个expression式需要放在圆括号中,因为从C语法中略微突破来使用C ++特性就足够了。 事情把我们带到零论点的地步。
构造函数中的零参数
你可能已经注意到我的解释中的eighty_four
function。
int eighty_four();
是的,这也是最令人头疼的parsing。 这是一个有效的定义,如果你创build了头文件(你应该),你很可能已经看到了这个定义。 添加括号不能解决它。
int eighty_four(());
为什么? 那么, ()
不是一个expression式。 在C ++中,你必须在括号之间放置一个expression式。 你不能在C ++中编写auto value = ()
,因为()
并不代表任何东西(甚至如果是空元组(参见Python),它将是一个参数,而不是零)。 实际上,这意味着如果不使用C ++ 11的语法,则不能使用简写语法,因为没有要放在括号中的expression式,并且函数声明的C语法将始终适用。
你可以改为
A a(());
使用
A a=A();
在你的例子中,最内层的parens是一个expression式,而在C ++中,语法定义了一个expression
作为assignment-expression
或者另外一个expression
后跟一个逗号和另一个assignment-expression
(附录A.4 – 语法摘要/expression式)。
语法进一步定义了assignment-expression
作为其他几种expression式之一,其中没有一个可以是assignment-expression
(或者只有空白)。
所以你不能有A a(())
原因只是因为语法不允许。 然而,我无法回答为什么创buildC ++的人不允许这种特殊的空对象的使用,我猜他们宁愿不把这种特殊情况下,如果有一个合理的select。