6

The following code does not compile in gcc:

namespace One{
   class A{
   };
};

namespace Two{
   class A{
      public:
         void what(){
            cout << "Two::A says what!" << endl;
         }
   };

   class B : public One::A{
      public:
         B(){
            A xx;
            xx.what();
         }
   };

};

And it gives:

gccbug.cpp: In constructor ‘Two::B::B()’:
gccbug.cpp:23: error: ‘class One::A’ has no member named ‘what’

Now, I was told that this is correct behavior (due to injected base name of One::A making A refer to One::A). However, this code compiles in C# (well, after changing a few things), so this seems to be c++ specific.

What I'm wondering is.. why? Is there a specific purpose for injecting the base name "One::A" as "A"?

kamziro
  • 7,882
  • 9
  • 55
  • 78
  • 6
    Just for reference, C# != C++. I'm not sure why you're comparing the two – Tony The Lion Feb 26 '12 at 11:37
  • I can't say for sure (I don't have my copy with me right now), but as an aside, questions about why the C++ language was designed the way it was are often answered in a book called "The Design and Evolution of C++" by Stroustrup. – Stuart Golodetz Feb 26 '12 at 12:07
  • @TonyTheLion, Well, it's the other c-like programming language that has "namespace", and one which I had access to a compiler to. I'm not saying there's only "One True Way" in which this is correct, but this did not seem intuitive, at least given this example. – kamziro Feb 26 '12 at 12:28
  • 1
    @Tony The Lion Isn't the fact that C# != C++ what makes it _worth_ comparing them? – jogojapan Feb 26 '12 at 12:41

3 Answers3

3

Is there a specific purpose for injecting the base name "One::A" as "A"?

Yes. It is so that you could write this:

namespace N
{
   class A
   {
       A *a;
   };
}

In the absence of injected-name, you've to write N::A *a which is not nice.

Note that it is because of injected-name, the following lines are allowed:

A::A *a1; //ok
A::A::A *a2; //ok 
A::A::A::A *a3; //ok 
A::A::A::A::A *a4; //ok 
//and so on

Online demo

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • Isn't this different from the situation explained by the asker? In the original example `class B` was inside one namespace, but the class it is derived from was in another. – jogojapan Feb 26 '12 at 11:51
  • 1
    @jogojapan But `class B` inherits all the names from its base class, including the injected name of the base class itself. Being in an inner scope, it hides names from the surrounding namespace. – Bo Persson Feb 26 '12 at 12:22
  • @Bo Persson Yes. I am just pointing out that the above example does not quite illustrate this point. In the above example the only reference to `class A` is inside the same namespace as the declaration of the class itself. You don't need name injection for that. – jogojapan Feb 26 '12 at 12:29
  • @jogojapan: You need name injection for that too. Otherwise, why does `A::A::A::A *a` work? – Nawaz Feb 26 '12 at 12:42
  • Ok, granted. There is name injection _at work_ there. The key difference I see between what I thought the question was about and the example above is that the question demonstrates that the compiler _prefers_ `One::A` over `Two::A`, rather than merely pointing out the name clash. What is the motivation for that preference? – jogojapan Feb 26 '12 at 13:36
  • @jogojapan: After my example code, there follows a natural conclusion : if `N::A *a` is not a nice syntax when declaring a member variable of `A` itself (means, the natural syntax should rather be `A *a` to convey the same meaning), then it becomes pretty much obvious why injected-name are preferred over any other name. I mean it would be absurd if the syntax `A *a` in the class `A` declares a pointer variable of type other than `A`. Don't you think so? – Nawaz Feb 26 '12 at 14:07
  • 1
    Well, given that there are two types called `A` in the original question, one is the base class but in a different namespace and the other is not the base class but in the same namespace, I still think it's a very ambiguous situation. But I am grateful (+1) for what I learnt through the discussion. – jogojapan Feb 27 '12 at 04:23
3

The only reason I can think of is that in C++ you are likely to refer to the base class name in the initializer list of the constructor, like this:

namespace Two {

  /*...*/

  class B : public One::A {
  public:
     B():A()
     {
        /*...*/
     }
   };
}

Of course the purpose then is different from the one in your example, because you actually declare a local variable inside the constructor, whereas in my example, the A() refers to the object of type A that is implicit in the definition of class B due to inheritance.

However, the situation of my example is more likely to occur, so I guess they thought let's not require the namespace to be made explicit in this case. As a consequence, any reference to A without a namespace is interpreted as referring to the base class, rather than any other class named A, even if it is in the same namespace as the declaration of B.

jogojapan
  • 68,383
  • 11
  • 101
  • 131
0

By qualifying A with One:: you added the A from namespace one in scope, so the compiler will look there for it's name resolution.

Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • But the object of type `A` declared inside the constructor has not been qualified with `One::`. It is interpreted as belonging to `namespace One`, although the declaration of `class B` itself is actually inside `namespace Two`. – jogojapan Feb 26 '12 at 11:48