g++ 拒绝,clang++ 接受: foo(x)( "bar" )( "baz" );
g++ rejects, clang++ accepts: foo(x)("bar")("baz");
前几天有人问为什么有些东西用 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
我所知,这在歧义决议6.8
C++标准部分草案中有所涉及,该部分说表达式语句和声明之间可能存在歧义,并说:
涉及表达式语句的语法存在歧义 和声明:具有函数样式的表达式语句 显式类型转换 (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 的值作为 prvalue。
和 8.3
声明符的含义,其中说:
在声明 T D 中,其中 D 的形式为
( D1 )
包含的声明符 ID 的类型与 声明中包含声明符 ID
T D1
括号不会更改嵌入式声明符 ID 的类型,但 它们可以更改复杂声明符的绑定。
更新
我最初使用的是 N337,但如果我们查看更新6.8
N4296 部分,它现在包括以下注释:
如果语句在语法上不能是声明,则没有歧义,因此此规则不会 应用。
这意味着gcc
不正确,因为:
foo x ("bar")("baz");
不能是一个有效的声明,我最初将第2
段解释为,如果你的情况以以下任何一项开头,那么它就是声明,这也许也是gcc
实现者的解释。
我本应该对第2
段更加怀疑,因为第2
段唯一的规范性部分实际上对第1
段没有任何规定,而且似乎对一个不具有规范性的例子提出了要求。我们可以看到,第2
段中的那句话现在实际上是一个更有意义的注释。
正如T.C.在下文中指出的,第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上讨论的这个g ++错误非常相似; 似乎 G++ 在处理过程中可能过早地决定该行必须是声明。
- ld: foo.o:foo.h:6 'bar' 的多重定义 foo.h:6: 首先在这里定义
- C++ "var foo = !!::bar()"语法?
- 诸如class bar *bar之类的指针的名称是什么?
- 无法将样式添加到QT Tab小部件中的选项卡(bar)和Tab(窗格)
- What does int foo = bar << ' '; mean in c++?
- 桌带(Explorer Bar)视觉样式
- Win32 API C++ Menu bar
- C++ vertical bar?
- 在"bar::foo"的定义中使用"foo"
- 为什么 foo(short int *) 和 bar(signed short *) 认为 QT 的签名不同
- 为什么我需要双括号在构造函数调用中,如:foo x((bar());
- g++ 拒绝,clang++ 接受: foo(x)( "bar" )( "baz" );
- 错误:类型为"bar&"的参数的默认参数<int>具有类型"bar<int>"
- 在 C++11 中使用 auto foo = "bar" vs std::string
- 为什么"Foo f(Bar());"可以是采用 Bar 类型并返回类型 Foo 的函数的声明?
- 将 'bar 类型的向量复制到 BYTE 类型的向量
- Blackberry 10正在安装.bar文件
- What is an `int foo::*bar::*`?
- 未解析的外部符号"public: class Foo __thiscall Bar::bu(char const *)"
- 在C++中,foo::bar::baz()的含义是什么