为何C++允许类型与括号包裹的变量名声明间省略空格?
问题解答
你的理解完全准确:
int(x) = 0;与int x = 0;完全等价,都是声明int类型变量x并初始化为0- 省略
*的Oit(bar) = 7;确实会被编译器解析为Oit bar = 7;,也就是对已声明过的bar做重复声明,这也是该行代码行为反直觉的根本原因。
你碰到的是C中著名的*最令人头疼的解析(Most Vexing Parse, MVP)*规则的典型场景:C标准明确规定,当一段代码存在两种合法解析可能——既可以解析为表达式,也可以解析为声明时,编译器必须优先选择声明的解析方式。
对你给出的示例代码来说:
using Oit = std::ostream_iterator<int>; Oit bar(std::cout); *bar = 6; // * 可省略 *Oit(bar) = 7; // 此处*不可省略!
如果最后一行去掉*写成Oit(bar) = 7;,两种解析路径分别是:
- 按表达式解析:构造一个Oit类型的临时对象,以bar为构造参数,再给该临时对象赋值7
- 按声明解析:声明一个名为bar的Oit类型变量,以7为初始化值,其中包裹bar的括号是声明符语法允许的冗余分组括号,不影响语义
按照MVP规则,编译器必须选择第二种解析,自然就出现了和预期不符的结果。而加上*之后,代码无法再被解析为合法声明——声明中指针修饰符*必须跟随在类型说明符之后、声明符之前,不可能出现在类型名Oit的前面,因此编译器只能将其解析为表达式:对Oit(bar)构造出的临时对象解引用后赋值7,符合预期。
至于你提到的“括号前省略空格”的规则,这并不是C专门为声明语法设计的特殊规则,而是C/C一脉相承的通用词法规则:词法分析阶段,空白字符(空格、制表符、换行等)的唯一作用是分隔独立的词法记号(token),只要不会导致两个独立token被错误合并为一个,空白的有无、数量多少完全不影响语义。
这套规则对所有语法结构生效:
a+b、a + b、a + b语义完全一致func(arg)、func (arg)、func (arg)语义完全一致- 放到声明场景下,
int (x)、int(x)、int (x)的token序列完全相同,语义自然没有任何区别。你觉得反直觉,本质是声明语法中“允许用括号包裹变量名”的设计,和“Type(name)构造临时对象”的表达式写法刚好撞车,才催生了这类歧义问题。
内容的提问来源于stack exchange,提问作者xdavidliu
相关产品推荐
相关产品推荐

