GCC 11.x 令人烦恼的解析 +不一致的错误(重新声明为不同的符号类型),这是 GCC 错误吗?
以下代码可以正常编译从 GCC 4.7.1 到但不包括 GCC 11.1:
constexpr int SomeValue = 0;
void test () {
void (SomeValue) ();
}
在 GCC 上11.x 失败:
<source>:4:23: error: 'void SomeValue()' redeclared as different kind of entity
4 | void (SomeValue) ();
| ^
<source>:1:15: note: previous declaration 'constexpr const int SomeValue'
1 | constexpr int SomeValue = 0;
| ^~~~~~~~~
但是错误“重新声明为不同类型的实体”对我来说似乎很奇怪:除了不明确的解析可能性之外,范围是不同的。此外,这些测试均在自 4.7.1(包括 11.x)以来的所有 GCC 版本上编译,即使据我所知,每个人都将 SomeValue
重新声明为“一种不同类型的实体”:
constexpr int SomeValue = 0;
void test1 () { typedef void (SomeValue) (); }
void test2 () { double SomeValue; }
void test3 () { using SomeValue = char *; }
void test4 () { void (* SomeValue) (); }
void test5 () { struct SomeValue { }; }
void test6 () { enum class SomeValue { }; }
作为一个相对不那么无意义的示例,此代码也失败了 从 11.x 开始以类似的方式:
constexpr int SomeValue = 0;
struct SomeClass {
explicit SomeClass (int) { }
void operator () () { }
};
void test () {
SomeClass(SomeValue)();
}
尽管在这种情况下,它前面有一个令人烦恼的解析警告,也在 11.x 之前不存在(警告在这里但不在上面的事实是有道理的,事实上警告在 11.x 之前不出现这一事实是有趣的一点):
<source>: In function 'void test()':
<source>:9:25: warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
9 | SomeClass(SomeValue)();
| ^~
<source>: At global scope:
<source>:9:26: error: 'SomeClass SomeValue()' redeclared as different kind of entity
9 | SomeClass(SomeValue)();
| ^
<source>:1:15: note: previous declaration 'constexpr const int SomeValue'
1 | constexpr int SomeValue = 0;
| ^~~~~~~~~
Compiler returned: 1
但是等等!还有更多!
这段代码——由于与上面相同的解析歧义,我预计它会在 11.x 上失败——在 所有这些版本的 GCC(包括 11.x):
constexpr int SomeValue = 0;
auto closure = [] (int) {
return [] () { };
};
void test () {
closure(SomeValue)(); // <-- doesn't cause any problems
}
没有警告或任何内容。
那么...这是怎么回事?为什么在这些特定情况下,只有 SomeValue
被“重新声明为不同类型的实体”才成为问题,并且仅从 GCC 11.1 开始,为什么 closure(SomeValue)( )
遇到与 SomeClass(SomeValue)()
相同的问题吗?
还有什么变化? GCC 这里正确吗?这是 GCC 11.x 中引入的新错误吗?或者也许是一个最终在 11.x 中修复的老错误?或者根本不是一个错误,而是其他东西发生了变化?
我正在努力想出一个一致的解释。
The following code compiles with no problems from GCC 4.7.1 up to but not including GCC 11.1:
constexpr int SomeValue = 0;
void test () {
void (SomeValue) ();
}
On GCC 11.x it fails with:
<source>:4:23: error: 'void SomeValue()' redeclared as different kind of entity
4 | void (SomeValue) ();
| ^
<source>:1:15: note: previous declaration 'constexpr const int SomeValue'
1 | constexpr int SomeValue = 0;
| ^~~~~~~~~
But the error "redeclared as different kind of entity" seems strange to me: Ambiguous parsing possibilities aside, the scope is different. Also, these tests all compile on all versions of GCC since 4.7.1 (including 11.x), even though AFAIK each one is redeclaring SomeValue
as "a different type of entity":
constexpr int SomeValue = 0;
void test1 () { typedef void (SomeValue) (); }
void test2 () { double SomeValue; }
void test3 () { using SomeValue = char *; }
void test4 () { void (* SomeValue) (); }
void test5 () { struct SomeValue { }; }
void test6 () { enum class SomeValue { }; }
As a relatively less nonsensical example, this code also fails from 11.x on in a similar fashion:
constexpr int SomeValue = 0;
struct SomeClass {
explicit SomeClass (int) { }
void operator () () { }
};
void test () {
SomeClass(SomeValue)();
}
Although in this case it's preceded by a vexing-parse warning that also isn't present before 11.x (the fact that the warning is here but not in the above makes sense, the fact that the warning doesn't appear pre-11.x is the interesting bit):
<source>: In function 'void test()':
<source>:9:25: warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
9 | SomeClass(SomeValue)();
| ^~
<source>: At global scope:
<source>:9:26: error: 'SomeClass SomeValue()' redeclared as different kind of entity
9 | SomeClass(SomeValue)();
| ^
<source>:1:15: note: previous declaration 'constexpr const int SomeValue'
1 | constexpr int SomeValue = 0;
| ^~~~~~~~~
Compiler returned: 1
But wait! There's more!
This code -- which I would have expected to fail on 11.x due to the same parsing ambiguities as above -- compiles just fine on all those versions of GCC (including 11.x):
constexpr int SomeValue = 0;
auto closure = [] (int) {
return [] () { };
};
void test () {
closure(SomeValue)(); // <-- doesn't cause any problems
}
No warnings or anything there.
So... What's going on here? Why is it only a problem for SomeValue
to be "redeclared as a different kind of entity" in those specific cases, and only since GCC 11.1, and why doesn't closure(SomeValue)()
suffer the same problem as SomeClass(SomeValue)()
?
Also what changed? Is GCC correct here? Is it a new bug introduced in GCC 11.x? Or perhaps an old bug that was finally fixed in 11.x? Or not a bug at all and something else changed?
I'm struggling to come up with a consistent explanation.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
不同之处在于,您的第一个代码片段声明了一个全局存在的函数;您的所有其他声明均属于本地实体。
(请注意,即使声明有效,您也无法调用该函数,因为它不存在。)
在最后一个片段中,
closure
不是类型,因此它不能一份声明。The difference is that your first snippet declares a function that exists globally; all your other declarations are of local entities.
(Note that even if the declaration were valid, you couldn't call that function, since it can't exist.)
In the last snippet,
closure
is not a type, so it can't be a declaration.