3

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);
}
TonySalimi
  • 8,257
  • 4
  • 33
  • 62
  • 2
    IIRC, only the inner-most inclosing namespace is searched for the class name, if you don't specify a namespace explicitly. So you created a `X::A_Creator` class. Maybe you can trick the compiler if you said `using ::A_Creator;` in namespace X. Regardless, the correct way is to change it to `::A_Creator` in the friend declaration. – Johannes Schaub - litb Dec 13 '19 at 17:28
  • @JohannesSchaub-litb But `A_Creator` has already been forward declared without any namespace. Why gcc cannot catch that? – TonySalimi Dec 13 '19 at 17:29
  • I already explained that only the innermost enclosing namespace is searched. But X has no A_Creator. – Johannes Schaub - litb Dec 13 '19 at 17:34
  • @JohannesSchaub-litb OK, is there anywhere in the standard that I can find this behavior? So, it means that VS2015 is not compliant with the standard, right? – TonySalimi Dec 13 '19 at 17:35
  • 2
    https://timsong-cpp.github.io/cppwp/n3337/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. – Language Lawyer Dec 13 '19 at 17:36
  • *Maybe* you can specify `friend A_Creator;` if you really want to avoid saying `::A_Creator` (C++11 onwards). Haven't tried. – Johannes Schaub - litb Dec 13 '19 at 17:39
  • 1
    @LanguageLawyer The answer box is below :-) – NathanOliver Dec 13 '19 at 17:40
  • @NathanOliver-ReinstateMonica [Johannes Schaub - litb](https://stackoverflow.com/users/34509/johannes-schaub-litb) was the first. – Language Lawyer Dec 13 '19 at 17:41
  • @LanguageLawyer I was lazy enough to not search for the spec ref. So you can claim all the points. – Johannes Schaub - litb Dec 13 '19 at 17:43
  • 1
    @Gupta "So, it means that VS2015 is not compliant with the standard, right?" - Correct. VS2015 is actually *quite far* from conforming with the standard. VS2019 is *much* better in that regard (especially after you enable C++17, various `/Zc:` options and `/permissive-`, and `/volatile:iso` and a few more that I don't currently remember). – Jesper Juhl Dec 13 '19 at 17:45

1 Answers1

5

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
    };
  };
}
Language Lawyer
  • 3,378
  • 1
  • 12
  • 29