Clang 给出以下警告消息:
<source>:12:16: warning: parentheses were disambiguated as redundant parentheses around declaration of variable named 'num' [-Wvexing-parse]
Boo(num); // No default constructor
^~~~~
这是一个最令人烦恼的解析问题。因为Boo
是类类型的名称,并且num
不是类型名称,Boo(num);
可以是临时类型的构造Boo
with num
被争论Boo
的构造函数或者它可以是一个声明Boo num;
在声明符周围有额外的括号num
(声明者可能总是有)。如果两者都是有效的解释,则标准要求编译器采用声明。
如果它被解析为声明,那么Boo num;
将调用默认构造函数(不带参数的构造函数),该构造函数不是由您声明的,也不是隐式声明的(因为您声明了另一个构造函数)。因此该程序是格式错误的。
这不是问题Boo(8);
, 因为8
不能是变量的标识符(declarator-id),因此它被解析为创建变量的调用Boo
暂时与8
作为构造函数的参数,因此不会调用默认构造函数(未声明),而是调用您手动定义的构造函数。
您可以使用以下任一方法消除声明中的歧义:Boo{num};
代替Boo(num);
(因为{}
不允许在声明符周围),通过将临时变量设为命名变量,例如Boo temp(num);
,或者将其作为另一个表达式中的操作数,例如(Boo(num));
, (void)Boo(num);
, etc.
请注意,如果默认构造函数可用,则声明将是格式良好的,因为它位于if
的分支块作用域而不是函数的块作用域,并且只会隐藏num
在函数的参数列表中。
无论如何,滥用临时对象创建来进行正常(成员)函数调用似乎都不是一个好主意。
这种在括号中带有单个非类型名称的特殊类型的最令人烦恼的解析只能发生,因为目的是创建一个临时对象并立即丢弃它,或者如果目的是创建一个直接用作初始化程序的临时对象,例如Boo boo(Boo(num));
(实际上声明了函数boo
接受一个名为num
与类型Boo
并返回Boo
).
通常不打算立即丢弃临时变量,并且可以使用大括号初始化或双括号来避免初始化程序情况(Boo boo{Boo(num)}
, Boo boo(Boo{num})
or Boo boo((Boo(num)));
, 但不是Boo boo(Boo((num)));
).
If Boo
不是类型名称,它不可能是声明,并且不会发生问题。
我还想强调一点Boo(8);
正在创建一个新的临时类型Boo
,甚至在类范围和构造函数定义内。正如人们可能错误地认为的那样,它不是使用调用者的构造函数来调用this
与通常的非静态成员函数类似的指针。不可能在构造函数体内以这种方式调用另一个构造函数。这仅在构造函数的成员初始值设定项列表中可行。
即使声明由于缺少构造函数而格式不正确,也会发生这种情况,因为[stmt.ambig]/3 http://eel.is/c++draft/stmt.ambig#3:
消歧纯粹是句法上的;也就是说,
在这样的陈述中出现的名字,无论它们是否是
类型名称与否,通常不被使用或更改
消歧义。
[...]
消歧先于解析,并且作为声明消歧的语句可能是格式错误的声明。
在编辑中修复:我忽略了所讨论的声明与函数参数处于不同的范围,因此如果构造函数可用,则声明的格式良好。在任何情况下,消歧过程中都不考虑这一点。还对一些细节进行了扩展。