C++11/14/17,GCC 7 与 GCC 8:朋友类模板的名称查找

Posted

技术标签:

【中文标题】C++11/14/17,GCC 7 与 GCC 8:朋友类模板的名称查找【英文标题】:C++11/14/17, GCC 7 vs GCC 8: Name lookup for friend class templates 【发布时间】:2018-06-13 15:46:26 【问题描述】:

我试图弄清楚以下代码在 GCC 7 中是否有效,但在 GCC 8.1 中无效。

代码的作用是:

定义(并转发声明)一个类模板MyGoodFriend(在全局命名空间中) 在inner命名空间内定义一个类模板BefriendedMyGoodFriend 的所有专业化成为Befriended 的朋友

有问题的部分是

        template<class FA>
        friend class MyGoodFriend;

我明白问题所在。 GCC 8.1 要求我在 friend 声明中使用完全限定名称 ::MyGoodFriend - 但是,GCC 7 对 MyGoodFriend 很满意。这是代码:

template<class A>
class MyGoodFriend;

namespace inner 
    template<class T>
    class Befriended 
    private:
        int i;
        T t;

        template<class FA>
        friend class MyGoodFriend;
        // This works for gcc 8.1:
        // template<class FA>
        //friend class ::MyGoodFriend;
    ;
 // namespace inner

template<class A>
class MyGoodFriend 
public:
    void do_something() 
        inner::Befriended<bool> bf;
        bf.i = 42;
    
;

int main() 
    MyGoodFriend<int> mgf;
    mgf.do_something();

您可以在此处使用 GCC 7 vs 8 进行测试:https://godbolt.org/g/6u9rgy

两个问题:

为什么 GCC 的行为发生了变化?

GCC 7 是否误解了标准?或者这是 GCC 8 中的一个错误?

如果 GCC 8 是正确的:为什么?

如果我正确阅读了标准(这里指的是 C++14 标准): 第 3.4 节(指定名称查找的工作方式)第 7.4 点指出:

X 类定义中使用的名称 […]

如果 X 是命名空间 N 的成员,或者是某个类的嵌套类 是 N 的成员,或者是本地类或本地内的嵌套类 作为 N 成员的函数的类,在定义之前 命名空间 N 或 N 的封闭命名空间之一中的类 X

显然,MyGoodFriend 是在封闭的命名空间中声明的,因此它应该在 Befriended 中可见 - 对吧?

感谢您的帮助!

【问题讨论】:

几个月前我见过类似的东西。 在您的示例中,我看不出 gcc 7 和 8 之间的行为有什么不同? @Barry 哦,是的,对不起!我简化了我的示例,使其不再使用 GCC 7 进行编译。关键是 Befriended 类也需要是一个模板。我更改了上面的代码! 【参考方案1】:

来自[namespace.memdef]/3,强调我的(C++11中的措辞相同):

如果非本地类中的友元声明首先声明了类、函数、类模板或函数模板99,则友元是最内层封闭命名空间的成员。 [...] 如果朋友声明中的名称既不是限定词也不是模板 ID,并且声明是函数或详细类型说明符,则用于确定实体是否先前已声明的查找不应考虑最里面的封闭命名空间之外的任何范围

也就是说,当你写的时候:

template<class FA>
friend class MyGoodFriend;

我们只寻找inner::MyGoodFriend,而不是::MyGoodFriend。由于我们没有找到它,我们认为它是类模板inner::MyGoodFriend 的前向声明。因此,::MyGoodFriend 不是 friended。


在修订版中,gcc 7 编译可能是因为许多与模板访问相关的错误之一。请参阅此meta-bug。 gcc 8 的行为是正确的。

【讨论】:

所以将其设为template&lt;class FA&gt; friend class ::MyGoodFriend; 可以解决问题。不错! 不是先查找inner::Befriend::Goodfriend吗? @Barry 很好——我没看到。但是,根据eel.is/c++draft/temp.names#nt:simple-template-id ,MyGoodFriend 应该是simple-template-id,因此是template-id,而… is neither qualified nor a template-id … 不应该满足,对吧?

以上是关于C++11/14/17,GCC 7 与 GCC 8:朋友类模板的名称查找的主要内容,如果未能解决你的问题,请参考以下文章

Centos 7/linux 安装gcc 教程

GCC 4.8.2 编译安装

CentOS6.5 gcc升级到4.8.2

Qt 4.8.4 mingw (gcc 4.7) 可以编译但不能运行

C语言关于数组指针的问题(linux下的gcc环境)

gcc 4.7.3 的一个 c++11 bug