Inn*_*der 25 c++ syntax g++ most-vexing-parse clang++
前几天有人曾问过为什么有些东西会与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;
}
Run Code Online (Sandbox Code Playgroud)
使用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";
Run Code Online (Sandbox Code Playgroud)
如果我在第21行添加一对括号,g ++很高兴:
(foo(x))("bar")("baz");
Run Code Online (Sandbox Code Playgroud)
换句话说,g ++将此行解释为:
foo x ("bar")("baz");
Run Code Online (Sandbox Code Playgroud)
用g ++来解决它的bug,但是我又想问一下标准专家,哪个编译器弄错了?
PS:gcc-4.8.3,clang-3.5.1
Sha*_*our 17
据我所知,C++标准部分6.8 Ambiguity解决方案涵盖了这一点,它表示表达式语句和声明之间可能存在歧义,并说:
在涉及表达式语句和声明的语法中存在歧义:具有函数式显式类型转换(5.2.3)的表达式语句,因为其最左侧的子表达式与第一个声明符以(a.)开头的声明无法区分.那些语句是声明.[注意:为了消除歧义,可能必须检查整个语句以确定它是表达式语句还是声明.这消除了许多例子的歧义.[示例:假设T是一个简单类型-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
Run Code Online (Sandbox Code Playgroud)
然后说:
其余案件是声明.[例如:
Run Code Online (Sandbox Code Playgroud)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- 末端示例] - 尾注]
似乎这种情况属于声明示例,特别是最后一个例子似乎在OP中出现这种情况,所以那样gcc就是正确的.
上面提到的相关部分5.2.3 显式类型转换(功能表示法)说:
[...]如果指定的类型是类类型,则类类型应完整.如果表达式列表指定多个单独的值,则类型应为具有适当声明的构造函数的类(8.5,12.1),并且表达式T(x1,x2,...)与声明T t等效. (x1,x2,......); 对于一些发明的临时变量t,结果是t的值作为prvalue.
和8.3 声明者的意思说:
在TD的声明中,D表格
Run Code Online (Sandbox Code Playgroud)( D1 )包含的declarator-id的类型与声明中包含的declarator-id的类型相同
Run Code Online (Sandbox Code Playgroud)T D1括号不会改变嵌入式声明符id的类型,但它们可以改变复杂声明符的绑定.
更新
我最初使用的是N337,但如果我们看一下N4296部分6.8已更新,它现在包含以下注释:
如果语句在语法上不能成为声明,则不存在歧义,因此该规则不适用.
这意味着gcc不正确,因为:
foo x ("bar")("baz");
Run Code Online (Sandbox Code Playgroud)
不能是一个有效的声明,我最初解释段落2说如果你的案例以下列任何一个开头那么它就是声明,这也许是gcc实现者如何解释的.
我应该对段落更加怀疑,2因为段落的唯一规范部分对段落2没有任何说明,1似乎对一个非规范性的例子提出要求.我们可以看到,声明形式段落2现在实际上是一个更有意义的注释.
正如TC在下面提到的那样,段落2实际上从来都不是规范性的,它只是以这种方式出现,并且与修正它的变化联系在一起.
如果我们删除该行
std::string x = "foo";
Run Code Online (Sandbox Code Playgroud)
然后g ++抱怨:
foo(x)("bar")("baz");
Run Code Online (Sandbox Code Playgroud)
语法错误:
foo.cc:20:18: error: expected ',' or ';' before '(' token
foo(x)("bar")("baz");
Run Code Online (Sandbox Code Playgroud)
我不知道怎么foo (x)("bar")("baz");可能是一个有效的声明,显然g ++也不能.该行foo x("bar")("baz");被拒绝并出现相同的错误.
Shafik的帖子中提到的"歧义解决方案"仅在表达式语句在语法上与声明无法区分时启动.但是在这种情况下,它不是一个有效的声明语法,因此没有歧义,它必须是一个表达式语句.
g ++无法将该行作为表达式语句处理,因此它是一个g ++错误.
这与最近在SO上讨论过的这个g ++ bug很相似; 似乎g ++可能在处理过程中决定该行必须是声明.
| 归档时间: |
|
| 查看次数: |
514 次 |
| 最近记录: |