Search code examples
c++c++11namespaceslanguage-lawyerfriend

Why gcc cannot detect friend class namespace from its forward declaration?


I have the following code that is compiling fine with VS2015, but not with gcc (any version). To me, the namespace of class A_Creator is properly defined (i.e. root namespace) because is has been forward declared on top of the program. Why gcc cannot detect the namespace of the A_Creator class properly? Which compiler is right?

#include <list>

class A_Creator;

namespace X
{
class A
{
private:
   int mX;

   A(int x) :
      mX(x)
   {}

   // GCC complains about this line, and it should be changed to ::A_Creator
   // On VS2015, both of them are working
   friend class A_Creator; 
};

} // namespace X

class A_Creator
{
public:

   std::list<X::A> TestOnList(int z)
   {
      std::list<X::A> a_list;
      a_list.push_back(X::A(z));

      return a_list;
   }
};


int main()
{
   A_Creator a_cr;
   auto x = a_cr.TestOnList(12);
}

Solution

  • From C++11 [namespace.memdef]/3:

    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. [Note: The other forms of friend declarations cannot declare a new member of the innermost enclosing namespace and thus follow the usual lookup rules. — end note]

    Since you have elaborated-type-specifier in the friend declaration (friend class A_Creator;), previous declarations shall be searched only in the innermost enclosing namespace ::X. So, gcc is right.
    A relevant excerpt from the Example in [namespace.memdef]/3, with global forward declaration of a function instead of a class:

    void h(int);
    namespace A {
      class X {
        class Y {
          friend void h(int);       // A::h is a friend
                                    // ::h not considered
        };
      };
    }