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

Luk*_*rth 7 c++ standards gcc c++17

我试图找出以下代码在GCC 7中工作,但在GCC 8.1中没有.

代码的作用是:

  • 定义(和转发声明)一个类模板MyGoodFriend(在全局命名空间中)
  • Befriendedinner命名空间内定义一个类模板
  • MyGoodFriend一个朋友的所有专业Befriended

有问题的是

        template<class FA>
        friend class MyGoodFriend;
Run Code Online (Sandbox Code Playgroud)

我明白了问题所在.GCC 8.1要求我::MyGoodFriendfriend声明中使用完全限定的名称- 但是,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();
}
Run Code Online (Sandbox Code Playgroud)

您可以在此处使用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中的类X或在N的封闭名称空间之一中

显然,MyGoodFriend是在一个封闭的命名空间中声明,所以它应该在Befriended- 右边可见?

谢谢你的帮助!

Bar*_*rry 10

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

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

也就是说,当你写:

template<class FA>
friend class MyGoodFriend;
Run Code Online (Sandbox Code Playgroud)

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


随着修订,gcc 7编译可能是因为许多模板访问相关的错误之一.看到这个meta-bug.gcc 8的行为是正确的.