Type(::x);是有效的吗?

23
在讨论 Type(identifier); 语法及其声明方式时,我遇到了 Type(::x); 在 Clang 中无法工作的情况。我期望对于一个全局变量 x,它会将 ::x 视为表达式(::x + 2 是可以工作的),并将 ::x 强制转换为 Type。但实际上,它输出编译器错误信息。

这是一个 简短的示例

int x;

int main() {
    int(::x); //does not compile
    int(::x + 2); //compiles
}

Clang 3.5 给出的编译器错误是:

错误:定义或重新声明的 'x' 不能命名全局作用域

然而,GCC 4.9.0 可以正常编译此代码。这段代码是否有效?

2个回答

19
据我所知,这是由C++草案标准8.3节“声明符的含义”中的第6段涵盖的(接下来是我的强调):

在声明T D中,其中D的形式为

( D1 )

包含的声明符ID的类型与声明

T D1

中包含的声明符ID的类型相同。

括号不会改变嵌入式声明符ID的类型,但它们可以改变复杂声明符的绑定。

因此:
int(::x);

等价于:

int ::x ;

这显然是无效的,会产生相同的错误。因此,gcc 4.9在这里是不正确的,但由于后来发布的gcc 4.8.3已经解决了这个问题,我期望这个问题在以后的版本中也得到了解决。4.9。尽管我没有在gcc 4.8.3 bugs fixed list中找到任何明显的匹配项,但他们没有声称这是一个完整的列表。
第二种情况是一个功能显式类型转换,它包含在5.2.3节的Explicit type conversion (functional notation)中,其中说:

一个简单类型说明符(7.1.6.2)或类型名说明符(14.6)后跟括号表达式列表构造给定表达式列表的指定类型的值。如果表达式列表是单个表达式,则类型转换表达式在定义性和如果定义在意义上等价于相应的强制转换表达式(5.4)。[...]

这是明确的,因为::x + 2是一个expression
涵盖语句何时被视为声明或表达式的部分是6.8节Ambiguity resolution,其中说:

语法中涉及表达式语句和声明存在歧义:将函数样式显式类型转换(5.2.3)作为其最左子表达式的表达式语句可能无法与以(开头的第一个声明符)开始的声明区分开来。在这些情况下,该语句是一个声明。[注意:要消除歧义,可能需要检查整个语句以确定它是表达式语句还是声明。这消除了许多例子。

并提供以下示例:
T(a)->m = 7; // expression-statement
T(a)++; // expression-statement
T(a,5)<<c; // expression-statement
T(*d)(int); // declaration
T(e)[5]; // declaration
T(f) = { 1, 2 }; // declaration
T(*g)(double(3)); // declaration
注意:如果T是一个类,则没有(),那么T ::D就是一个qualified-id,这在5.1的语法中涵盖了Primary expressions
更新
提交了gcc错误报告
gcc的回复是:
当前G++和EDG都将其视为有效表达式(int)::x
由于此回复暗示了clang是不正确的(虽然我不同意),因此我提交了clang错误报告,并且旧错误报告看起来相似并且似乎与gcc的回复不一致。
更新2
针对clang错误报告,Richard Smith表示应将其视为声明,并表示:
这并不意味着clang是不正确的;实际上,就我所见,Clang是正确的。(我也向EDG发送了错误报告。) 话虽如此,我们应该在这种情况下给出适当的“您遇到了令人烦恼的解析,以下是如何消除歧义”的错误。
更新3 gcc确认这是一个错误。

你知道这部分说的是无论声明是否有效,都必须将其视为声明而不是表达式吗?附注:如果struct Foo {};被用作类型,错误会发生变化 :) - chris
1
在“声明”T D(我强调)。在应用此条款之前,我们必须确定int(::x);是一个声明。 - M.M
Foo::x;Foo (::x); 给出不同的错误,其中 Foo 是一个类。 - M.M
@MattMcNabb 这是因为在 Foo::x 的情况下,空格被移除并且它被视为限定符标识符。 - Shafik Yaghmour
1
@MattMcNabb FYI,gcc确认这是一个bug。 - Shafik Yaghmour
显示剩余13条评论

11

这对我来说似乎是最棘手的解析。如果可以将其解析为声明,那么就会被解析为声明。第一个可能被解析为int变量的声明(int ::x;),但在该上下文中::是非法的。第二个必须是表达式,因此编译器会执行计算,将其转换为int类型,并丢弃结果。

这是一个学究式的问题,还是另有问题?如果您有特定的问题需要解决,提供更多信息将允许针对您的用例进行解决方案。


1
@Matt McNabb 这就是C++语言的工作方式。这也是我们需要在模板中使用“typename”关键字的原因。 - Mark B
我不清楚 int::x; 应该是什么意思。Foo::x; 会在 Foo 的作用域中查找 x,而这不是一个声明。似乎 int::x;int(::x); 是不同的。 - M.M
有趣。我没有意识到一个无效的声明仍然适用于有效的表达式。 - chris
1
@MarkB 我们需要使用 typename 来指示标识符是表示类型还是变量。我不认为这与 int (::x); 的情况相似。规则是,如果它在语法上作为声明有效,则它是一个声明(即使它也是声明中的非法形式)。但是,我不认为 int (::x);(在块范围内)是一个有效的声明语法。 - M.M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接