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

C++11/14/17, GCC 7 vs GCC 8: Name lookup for friend class templates

我想知道以下代码在 GCC 7 中是否有效,但在 GCC 8.1 中无效。

代码的作用是:

有问题的部分是

        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 与 8 进行测试:https://godbolt.org/g/6u9rgy

两个问题:

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

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

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

如果我没看错标准(这里指的是C++14标准): 第 3.4 节(指定名称查找的工作方式),第 7.4 点指出:

A name used in the definition of a class X […]

  • if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or a nested class within a local class of a function that is a member of N, before the definition of class X in namespace N or in one of N ’s enclosing namespaces

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

感谢您的帮助!

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

If a friend declaration in a non-local class first declares a class, function, class template or function template99 the friend is a member of the innermost enclosing namespace. [...] If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.

也就是你写的时候:

template<class FA>
friend class MyGoodFriend;

我们只寻找 inner::MyGoodFriend,而不寻找 ::MyGoodFriend。由于我们没有找到它,我们认为它是 class 模板 inner::MyGoodFriend 的前向声明。因此,::MyGoodFriend 未被 friend 编辑。


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