clang bug? namespaced template class' friend

后端 未结 3 1197
不知归路
不知归路 2021-02-19 11:21

The following code which doesn\'t compile under clang but does under gcc and VS:

template class bar;

namespace NS
{
    template

        
相关标签:
3条回答
  • 2021-02-19 11:58

    From cppreference:

    Names introduced by friend declarations within a non-local class X become members of the innermost enclosing namespace of X, but they do not become visible to lookup (neither unqualified nor qualified) unless a matching declaration is provided at namespace scope, either before or after the class definition. Such name may be found through ADL which considers both namespaces and classes. Only the innermost enclosing namespace is considered by such friend declaration when deciding whether the name would conflict with a previously declared name.

    void h(int);
    namespace A {
      class X {
        friend void f(X); // A::f is a friend
        class Y {
            friend void g(); // A::g is a friend
            friend void h(int); // A::h is a friend, no conflict with ::h
        };
      };
      // A::f, A::g and A::h are not visible at namespace scope
      // even though they are members of the namespace A
      X x;
      void g() {  // definition of A::g
         f(x); // A::X::f is found through ADL
      }
      void f(X) {}       // definition of A::f
      void h(int) {}     // definition of A::h
      // A::f, A::g and A::h are now visible at namespace scope
      // and they are also friends of A::X and A::X::Y
    }
    

    It's not the standard, but it is in general correct. So clang seems to be right.

    0 讨论(0)
  • I believe that clang is correct. According to [namespace.memdef]/3:

    Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class, function, class template or function template the friend is a member of the innermost enclosing namespace.

    In your case, the name wouldn't appear to be "first declared" by the friend declaration. Later in that paragraph, however, emphasis mine:

    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.

    That is, this declaration:

    template<typename U> friend class bar;
    

    will not look for bar outside of namespace NS, so it will not find your earlier declaration. As such, it declares a class template NS::bar<typename > to be a friend of foo. You will have to qualify the name bar in order for it to be found:

    template<typename U> friend class ::bar;
    

    This seems related to GCC Bug 37804.

    0 讨论(0)
  • 2021-02-19 12:09

    Changing the code to

    template<typename T> class bar;
    
    namespace NS
    {
        template<typename T>
        class foo
        {
            foo() {}
    
            template<typename U> friend class ::bar;
        };
    }
    
    template<typename R>
    class bar
    {
    public:
        bar()
        {
            NS::foo<int> f;
        }
    };
    
    
    int main(int, char **)
    {
        bar<int> b;
    
        return 0;
    }
    

    compiles with clang. The problem seems to be the namespace lookup. The code

    template<typename U> friend class bar;
    

    actually declared the class NS::bar a friend of NS::foo, so foo should not have been affected. My guess would be that clang is standard conformant and the code should not compile.

    0 讨论(0)
提交回复
热议问题