g ++ rejects,clang ++接受:foo(x)("bar")("baz");

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)

然后说:

其余案件是声明.[例如:

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
Run Code Online (Sandbox Code Playgroud)

- 末端示例] - 尾注]

似乎这种情况属于声明示例,特别是最后一个例子似乎在OP中出现这种情况,所以那样gcc就是正确的.

上面提到的相关部分5.2.3 显式类型转换(功能表示法)说:

[...]如果指定的类型是类类型,则类类型应完整.如果表达式列表指定多个单独的值,则类型应为具有适当声明的构造函数的类(8.5,12.1),并且表达式T(x1,x2,...)与声明T t等效. (x1,x2,......); 对于一些发明的临时变量t,结果是t的值作为prvalue.

8.3 声明者的意思说:

在TD的声明中,D表格

( D1 ) 
Run Code Online (Sandbox Code Playgroud)

包含的declarator-id的类型与声明中包含的declarator-id的类型相同

T D1
Run Code Online (Sandbox Code Playgroud)

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

更新

我最初使用的是N337,但如果我们看一下N4296部分6.8已更新,它现在包含以下注释:

如果语句在语法上不能成为声明,则不存在歧义,因此该规则不适用.

这意味着gcc不正确,因为:

foo x ("bar")("baz");
Run Code Online (Sandbox Code Playgroud)

不能是一个有效的声明,我最初解释段落2说如果你的案例以下列任何一个开头那么它就是声明,这也许是gcc实现者如何解释的.

我应该对段落更加怀疑,2因为段落的唯一规范部分对段落2没有任何说明,1似乎对一个非规范性的例子提出要求.我们可以看到,声明形式段落2现在实际上是一个更有意义的注释.

正如TC在下面提到的那样,段落2实际上从来都不是规范性的,它只是以这种方式出现,并且与修正它的变化联系在一起.

  • `T(g)(h,2);`与`T(g)(h)(2)非常不同;` (2认同)

M.M*_*M.M 5

如果我们删除该行

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 ++可能在处理过程中决定该行必须是声明.