【问题标题】:g++ rejects, clang++ accepts: foo(x)("bar")("baz");g++ 拒绝,clang++ 接受: foo(x)("bar")("baz");
【发布时间】:2015-02-02 17:22:54
【问题描述】:

前几天有人告诉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

【问题讨论】:

  • PPS:使用 gcc-4.9.2 在ideone 上尝试过——同样的错误

标签: c++ syntax g++ clang++ most-vexing-parse


【解决方案1】:

据我所知,C++ 标准草案6.8歧义解决对此进行了介绍,该部分表示表达式语句和声明之间可能存在歧义,并表示:

涉及表达式语句的语法存在歧义 和声明:具有函数样式的表达式语句 显式类型转换(5.2.3)作为其最左边的子表达式可以是 与第一个声明符开始的声明没有区别 带有 (。在这些情况下,该语句是一个声明。 [注意:要 消除歧义,可能需要检查整个陈述以 确定它是表达式语句还是声明。这 消除许多例子的歧义。 [ 示例:假设 T 是 简单类型说明符(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

—结束示例]—结束注释]

似乎这种情况属于声明示例,特别是最后一个示例似乎在 OP 中提出了这种情况,所以gcc 那时是正确的。

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

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

8.3 声明符的含义说:

在声明 T D 中,其中 D 具有以下形式

( D1 ) 

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

T D1

括号不会改变嵌入的 declarator-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

【讨论】:

  • 谢谢沙菲克。创建一个临时的foo(x) 是否属于函数式显式类型转换?我不应该这样想……
  • @InnocentBystander:不,我认为这是一个简单的声明。它被解析为foo x;
  • @InnocentBystander 添加了来自 5.2.3 的更多详细信息。
  • T(g)(h,2);T(g)(h)(2); 非常不同
  • foo(x)("bar") 是一个有效的声明。 foo(x)("bar")("baz"); 不可能。
【解决方案2】:

如果我们删除该行

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++ 在处理该行必须是声明时决定为时过早。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-04
    • 2012-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多