下面显示的错误消息是 gcc 13.1.0 bug 吗? 此代码在 clang 18.0.2 和 MSVC 19.41 下编译良好。 如果将
s0
更改为非模板类,此代码甚至可以在 gcc 上编译正常。 s0
作为一个模板类,有一些特殊之处让 gcc 感到不安。
目标是派生类
s1
应该有自己的类模板foo
,它完全隐藏来自foo
的任何s0
。 然后在声明 foo
后,s1
还想指定 s1::foo<T>
是 s1
的好友。 看起来 gcc 将 s1
的第一行解释为 s0<int>::foo<T>
的部分特化,但同样(奇怪的是)只有当 s0
是模板类时。
template< class X >
struct s0
{
template< class T >
struct foo;
};
struct s1 : public s0<int>
{
// - this should be an entirely new class template "foo"
// - hiding the "foo" in s0<int>
// [basic.scope.hiding]
//
template< class T >
struct foo;
// - gcc error is:
// partial specialization 's0<int>::foo<T>' declared 'friend'
// (gcc thinks the line above is a partial specialization??)
//
template< class T >
friend
struct foo;
};
如果这是一个错误,有什么解决方法吗?
好的,感谢@Jarod42、@3CxEZiVlQ 和@user17732522,我们在不查看 gcc 源代码的情况下得到了尽可能多的答案:
friend
是不必要的,因为嵌套类已经是其包含类的隐式友元。耶! 删除永远是最好的解决方案。friend
声明中查找名称的范围不是 s1
类范围,而是“最内部的封闭命名空间范围”(dcl.meaning.general.2.3)。 因此,friend
第二行上的s1
声明将“直接查看”foo
中新符号s1
的任何声明。 事实上,如果删除 s1
的第一行,错误仍然存在。foo
,而全局范围中没有 foo
,但实际上从编译器错误消息中我们可以看到 gcc 认为我们正在尝试到 friend
专业化 s0<int>::foo<T>
,因此 gcc 显然正在寻找 s0
。 不确定这是否正确,但是 basic.lookup.unqual 中有一个非规范注释说“当搜索类范围时,也会搜索其基类的范围([class.member.lookup ])。
如果它继承自单个基类,则就好像基类的范围立即包含派生类的范围,”这可以证明 gcc 的选择是合理的(但不是规范性的)。在澄清它的规范?friend
声明没有声明任何专门化,如 this 相关的,修复了有关注入类名的 gcc bug 中提到的)。 也许这可能只是一个错误的诊断,可能与将 foo
名称巧妙(错误)解释为
s0<int>::foo<T>
并将其视为与这个答案中类似(但不相同)的方式有关。