Stack Overflow教给我的很多东西都是所谓的"最令人烦恼的解析",经典地用一条线来证明
A a(B()); //declares a function
Run Code Online (Sandbox Code Playgroud)
虽然这对于大多数人而言,直观地看起来是a
类型对象的声明A
,将临时B
对象作为构造函数参数,它实际上是一个函数a
返回的声明A
,将一个指针指向一个返回的函数,它B
本身不带参数.同样的线
A a(); //declares a function
Run Code Online (Sandbox Code Playgroud)
也属于同一类别,因为它代替一个对象,它声明了一个函数.现在,在第一种情况下,这个问题的通常解决方法是在其周围添加一组额外的括号/括号B()
,因为编译器会将其解释为对象的声明
A a((B())); //declares an object
Run Code Online (Sandbox Code Playgroud)
但是,在第二种情况下,执行相同操作会导致编译错误
A a(()); //compile error
Run Code Online (Sandbox Code Playgroud)
我的问题是,为什么?是的我非常清楚正确的"解决方法"是将其更改为A a;
,但我很想知道()
第一个示例中的额外功能是什么,然后在重新应用它时不起作用第二个例子.A a((B()));
变通办法是否是写入标准的特定异常?
Bri*_*ndy 70
没有开明的答案,只是因为它没有被C++语言定义为有效的语法......所以它是如此,根据语言的定义.
如果你有一个表达式,那么它是有效的.例如:
((0));//compiles
Run Code Online (Sandbox Code Playgroud)
更简单的put:因为(x)
是有效的C++表达式,而()
不是.
要了解有关如何定义语言以及编译器如何工作的更多信息,您应该了解正式语言理论或更具体的上下文无关语法(CFG)和相关材料(如有限状态机).如果你对此感兴趣,虽然维基百科页面还不够,你将不得不得到一本书.
Dav*_*vid 37
此问题的最终解决方案是,如果可以,请转到C + 11统一初始化语法.
A a{};
Run Code Online (Sandbox Code Playgroud)
http://www.stroustrup.com/C++11FAQ.html#uniform-init
Kon*_*ski 29
首先,有C.在C中,A a()
是函数声明.例如,putchar
有以下声明.通常,这样的声明存储在头文件中,但是如果您知道函数声明的样子,那么没有什么能阻止您手动编写它们.参数名称在声明中是可选的,因此我在此示例中省略了它.
int putchar(int);
Run Code Online (Sandbox Code Playgroud)
这允许您编写这样的代码.
int puts(const char *);
int main() {
puts("Hello, world!");
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
正如我所提到的,C允许在头文件中省略参数名称,因此在头文件中output_result
看起来像这样.
int output_result(int());
Run Code Online (Sandbox Code Playgroud)
你不认识那个吗?好吧,让我提醒你.
A a(B());
Run Code Online (Sandbox Code Playgroud)
是的,这是完全相同的功能声明.A
是int
,a
是output_result
,B
是int
.
您可以轻松地注意到C与C++的新功能之间的冲突.确切地说,构造函数是类名和括号,而替代声明语法()
代替=
.按照设计,C++试图与C代码兼容,因此它必须处理这种情况 - 即使几乎没有人关心.因此,旧的C功能优先于新的C++功能.声明语法尝试将名称作为函数进行匹配,然后在恢复到新语法之前将其作为()
失败.
如果其中一个特性不存在,或者具有不同的语法(如{}
在C++ 11中),那么对于带有一个参数的语法,这个问题就永远不会发生.
现在你可能会问为什么A a((B()))
有效.好吧,让我们output_result
用无用的括号声明.
int output_result((int()));
Run Code Online (Sandbox Code Playgroud)
它不会起作用.语法要求变量不在括号中.
<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token
Run Code Online (Sandbox Code Playgroud)
但是,C++需要标准表达式.在C++中,您可以编写以下代码.
int value = int();
Run Code Online (Sandbox Code Playgroud)
以下代码.
int value = ((((int()))));
Run Code Online (Sandbox Code Playgroud)
C++期望括号内的表达式是... well ...表达式,而不是C期望的类型.括号在这里没有任何意义.但是,通过插入无用的括号,C函数声明不匹配,并且可以正确匹配新语法(只需要表达式,例如2 + 2
).
当然有一个论点很好,但两个呢?并不是构造函数可能只有一个参数.带有两个参数的内置类之一是std::string
std::string hundred_dots(100, '.');
Run Code Online (Sandbox Code Playgroud)
这一切都很好(从技术上讲,如果它被写成,它会有最令人烦恼的解析std::string wat(int(), char())
,但是说实话 - 谁会写出来?但是我们假设这个代码有一个棘手的问题.你会认为你必须把它括号中的一切.
std::string hundred_dots((100, '.'));
Run Code Online (Sandbox Code Playgroud)
不是这样.
<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>::
^
Run Code Online (Sandbox Code Playgroud)
我不确定为什么g ++会尝试转换char
为const char *
.无论哪种方式,只使用一个类型的值调用构造函数char
.没有重载有一个类型的参数char
,因此编译器很困惑.您可能会问 - 为什么参数是char类型?
(100, '.')
Run Code Online (Sandbox Code Playgroud)
是的,,
这是一个逗号运算符.逗号运算符接受两个参数,并给出右侧参数.它并不是真的有用,但它是我的解释所知.
相反,要解决最令人烦恼的解析,需要以下代码.
std::string hundred_dots((100), ('.'));
Run Code Online (Sandbox Code Playgroud)
参数在括号中,而不是整个表达式.事实上,只有一个表达式需要在括号中,因为它足以从C语法中略微使用C++特性.事情将我们带到零论点.
你可能已经eighty_four
在我的解释中注意到了这个功能.
int eighty_four();
Run Code Online (Sandbox Code Playgroud)
是的,这也受到最令人烦恼的解析的影响.这是一个有效的定义,如果您创建了头文件,那么您很可能已经看过(并且您应该).添加括号不会解决问题.
int eighty_four(());
Run Code Online (Sandbox Code Playgroud)
为什么会这样?好吧,()
不是表达.在C++中,您必须在括号之间放置一个表达式.你不能用auto value = ()
C++ 编写,因为()
它没有任何意义(即使它像空元组一样(参见Python),它将是一个参数,而不是零).实际上,这意味着您不能在不使用C++ 11的语法的情况下使用简写语法{}
,因为没有表达式放在括号中,并且函数声明的C语法将始终适用.
use*_*149 12
你可以改为
A a(());
Run Code Online (Sandbox Code Playgroud)
使用
A a=A();
Run Code Online (Sandbox Code Playgroud)
你的例子中最内层的parens将是一个表达式,在C++中,语法定义expression
为一个assignment-expression
或另一个,expression
后跟逗号和另一个assignment-expression
(附录A.4 - 语法摘要/表达式).
语法进一步将其定义assignment-expression
为几种其他类型的表达式之一,其中没有一种可以是空的(或只有空格).
所以你不能拥有的原因A a(())
仅仅是因为语法不允许它.但是,我不能回答为什么创建C++的人不允许这种特殊用途的空白parens作为某种特殊情况 - 我猜他们宁愿不提出这样一个特殊情况,如果有的话一个合理的选择.
归档时间: |
|
查看次数: |
17465 次 |
最近记录: |