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
据我所知,这在 C++ 标准草案 6.8
歧义解决 中有所说明,其中表示表达式语句和声明之间可能存在歧义并说:
There is an ambiguity in the grammar involving expression-statements
and declarations: An expression statement with a function-style
explicit type conversion (5.2.3) as its leftmost subexpression can be
indistinguishable from a declaration where the first declarator starts
with a (. In those cases the statement is a declaration. [ Note: To
disambiguate, the whole statement might have to be examined to
determine if it is an expression-statement or a declaration. This
disambiguates many examples. [ Example: assuming T is 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
然后说:
The remaining cases are declarations. [ Example:
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
显式类型转换(函数符号)说:
[...] If the type specified is a class type, the class type shall be complete. If the expression
list specifies more than a single value, the type shall be a class with a suitably declared constructor (8.5, 12.1),
and the expression T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, x2, ...); for some
invented temporary variable t, with the result being the value of t as a prvalue.
和8.3
声明符的含义表示:
In a declaration T D where D has the form
( D1 )
the type of the contained declarator-id is the same as that of the
contained declarator-id in the declaration
T D1
Parentheses do not alter the type of the embedded declarator-id, but
they can alter the binding of complex declarators.
更新
我最初使用 N337 but if we look at N4296 部分 6.8
已更新,现在包括以下注释:
If the statement cannot syntactically be a declaration, there is no ambiguity, so this rule does not
apply.
这意味着 gcc
不正确,因为:
foo x ("bar")("baz");
不能是一个有效的声明,我最初将段落 2
解释为如果你的案例以以下任何一个开头那么它就是声明,这可能是 gcc
实现者解释为嗯。
我应该更怀疑段落 2
,因为段落 2
的唯一规范部分实际上对段落 1
什么也没说,而且似乎对不规范的例子。我们可以看到 2
段中的语句现在实际上是一个更有意义的注释。
如T.C。如下所述,2
段实际上从来都不是规范的,它只是那样出现,他 linked to the change that fixed it.
如果我们删除行
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 的 post 中提到的 "ambiguity resolution" 只有在表达式语句在句法上与声明无法区分时才会出现。但是在这种情况下,它不是有效的声明语法,因此没有歧义,它必须是表达式语句。
g++ 无法将该行作为表达式语句处理,因此这是一个 g++ 错误。
这与最近在 SO 上讨论的 非常相似;似乎 g++ 在处理该行必须是声明时可能决定得太早了。
前几天有人
#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
据我所知,这在 C++ 标准草案 6.8
歧义解决 中有所说明,其中表示表达式语句和声明之间可能存在歧义并说:
There is an ambiguity in the grammar involving expression-statements and declarations: An expression statement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration. [ Note: To disambiguate, the whole statement might have to be examined to determine if it is an expression-statement or a declaration. This disambiguates many examples. [ Example: assuming T is 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
然后说:
The remaining cases are declarations. [ Example:
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
显式类型转换(函数符号)说:
[...] If the type specified is a class type, the class type shall be complete. If the expression list specifies more than a single value, the type shall be a class with a suitably declared constructor (8.5, 12.1), and the expression T(x1, x2, ...) is equivalent in effect to the declaration T t(x1, x2, ...); for some invented temporary variable t, with the result being the value of t as a prvalue.
和8.3
声明符的含义表示:
In a declaration T D where D has the form
( D1 )
the type of the contained declarator-id is the same as that of the contained declarator-id in the declaration
T D1
Parentheses do not alter the type of the embedded declarator-id, but they can alter the binding of complex declarators.
更新
我最初使用 N337 but if we look at N4296 部分 6.8
已更新,现在包括以下注释:
If the statement cannot syntactically be a declaration, there is no ambiguity, so this rule does not apply.
这意味着 gcc
不正确,因为:
foo x ("bar")("baz");
不能是一个有效的声明,我最初将段落 2
解释为如果你的案例以以下任何一个开头那么它就是声明,这可能是 gcc
实现者解释为嗯。
我应该更怀疑段落 2
,因为段落 2
的唯一规范部分实际上对段落 1
什么也没说,而且似乎对不规范的例子。我们可以看到 2
段中的语句现在实际上是一个更有意义的注释。
如T.C。如下所述,2
段实际上从来都不是规范的,它只是那样出现,他 linked to the change that fixed it.
如果我们删除行
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 的 post 中提到的 "ambiguity resolution" 只有在表达式语句在句法上与声明无法区分时才会出现。但是在这种情况下,它不是有效的声明语法,因此没有歧义,它必须是表达式语句。
g++ 无法将该行作为表达式语句处理,因此这是一个 g++ 错误。
这与最近在 SO 上讨论的