为什么不A a(()); 工作?

在Stack Overflow教给我的很多东西中,被称为“最令人头痛的parsing”的东西,

A a(B()); //declares a function 

虽然这对大多数人来说,直观地看来是Atypes的对象的声明,将一个临时的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{}; 

http://www.stroustrup.com/C++11FAQ.html#uniform-init

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()); 

是的,这是完全相同的函数声明。 Aintaoutput_resultBint

你可以很容易地注意到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 * 。 无论哪种方式,只用一个chartypes的值来调用构造函数。 没有重载有chartypes的一个参数,所以编译器很困惑。 你可能会问 – 为什么这个参数是chartypes的?

 (100, '.') 

是的,这是一个逗号运算符。 逗号运算符有两个参数,并给出右边的参数。 这不是真的有用,但是我的解释是有用的。

相反,要解决最令人头痛的parsing,需要下面的代码。

 std::string hundred_dots((100), ('.')); 

参数在括号中,而不是整个expression式。 事实上,只有一个expression式需要放在圆括号中,因为从C语法中略微突破来使用C ++特性就足够了。 事情把我们带到零论点的地步。

构造函数中的零参数

你可能已经注意到我的解释中的eighty_fourfunction。

 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。