有一天有人asked为什么有些东西会用clang编译,而不是用gcc编译。我直观地理解了发生了什么,并且能够帮助这个人,但它让我想知道 - 根据标准,哪个编译器是正确的?这是代码的简化版本:
#include <iostream>
#include <string>
class foo
{
public:
foo(const std::string& x):
name(x)
{ }
foo& operator()(const std::string& x)
{
std::cout << name << ": " << x << std::endl;
return (*this);
}
std::string name;
};
int main()
{
std::string x = "foo";
foo(x)("bar")("baz");
return 0;
}
使用clang ++可以很好地编译,但是g ++会出现以下错误:
runme.cpp: In function ‘int main()’:
runme.cpp:21:11: error: conflicting declaration ‘foo x’
foo(x)("bar")("baz");
^
runme.cpp:20:17: error: ‘x’ has a previous declaration as ‘std::string x’
std::string x = "foo";
如果我在第21行添加一对括号,g ++很高兴:
(foo(x))("bar")("baz");
换句话说,g ++将此行解释为:
foo x ("bar")("baz");
在g ++中解决它的错误,但是我又想问标准专家,哪个编译器弄错了?
PS:gcc-4.8.3,clang-3.5.1
答案 0 :(得分:17)
据我所知,C ++标准部分6.8
歧义解决方案草案中涵盖了这一点,它表明表达式声明和声明之间可能存在歧义,并说:
涉及表达式语句的语法含糊不清 和声明:具有函数样式的表达式语句 显式类型转换(5.2.3),因为它最左边的子表达式可以 与第一个声明者开始的声明无法区分 (a。在这些情况下,声明是声明。[注意:To 消除歧义,整个声明可能需要进行审查 确定它是表达式语句还是声明。这个 消除许多例子的歧义。 [例子:假设T是a simple-type-specifier(7.1.6),
并给出以下示例:
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
然后说:
其余案件是声明。 [例如:
class T { // ... public: T(); T(int); T(int, int); }; T(a); // declaration T(*b)(); // declaration T(c)=7; // declaration T(d),e,f=3; // declaration extern int h; T(g)(h,2); // declaration
-end example] -end note]
似乎这个案例属于声明示例,特别是最后一个例子似乎在OP中提出了这个案例,所以 gcc
就是正确的。
上面提到的相关部分5.2.3
显式类型转换(功能表示法)说:
[...]如果指定的类型是类类型,则类类型应完整。如果表达 list指定多个值,该类型应为具有适当声明的构造函数的类(8.5,12.1), 表达式T(x1,x2,...)与声明T t(x1,x2,...)有效;对于一些 发明了临时变量t,结果是t作为prvalue的值。
和8.3
声明者的含义,其中包含:
在声明T D中,D的格式为
( D1 )
包含的declarator-id的类型与 声明中包含声明者id
T D1
括号不会改变嵌入式声明符id的类型,但是 他们可以改变复杂声明者的绑定。
更新
我最初使用N337但如果我们查看N4296部分6.8
已更新,则现在包含以下注释:
如果语句在语法上不能成为声明,则不存在歧义,因此该规则不会 应用
这意味着gcc
不正确,因为:
foo x ("bar")("baz");
不能是一个有效的声明,我最初解释段落2
如果你的案例以下列任何一个开头那么它就是声明,这也许是gcc
实现者如何解释的。
我应该更加怀疑第2
段,因为第2
段的唯一规范部分对第1
段没有任何说明,似乎对一个例子提出了要求。不是规范性的。我们可以看到段落2
中的陈述现在实际上是一个更有意义的说明。
作为T.C.如下所述,第2
段实际上从来都不是规范性的,只是出现了这种方式而且linked to the change that fixed it。
答案 1 :(得分:5)
如果我们删除该行
std::string x = "foo";
然后g ++抱怨:
foo(x)("bar")("baz");
语法错误:
foo.cc:20:18: error: expected ',' or ';' before '(' token
foo(x)("bar")("baz");
我不知道foo (x)("bar")("baz");
如何成为有效的声明,显然g ++也不能。第foo x("bar")("baz");
行被拒绝并出现同样的错误。
Shafik的帖子中提到的“模糊性解决方案”仅在表达式语句在语法上与声明无法区分时启动。但是在这种情况下,它不是一个有效的声明语法,因此没有歧义,它必须是一个表达式语句。
g ++无法将该行作为表达式语句处理,因此它是一个g ++错误。
这与最近在SO上讨论的this g++ bug非常类似;似乎g ++可能在处理过程中决定该行必须是声明。