Home > Software design >  How does c decide which namespace to pick if it is omited
How does c decide which namespace to pick if it is omited

Time:10-24

Why was the last A picked from ns1 namespace instead of ns2?
(Bar is of type ns1::A<ns1::A<ns2::A, ns2::B>, ns2::B>)

namespace ns1 {
  template <typename T, typename U>
  struct A {};
}

namespace ns2 {
  struct /*ns2*/ A : ns1::A<int, /*ns2*/ A> {
    typedef ns1::A<int, /*ns2*/ A> Foo;
  };
  struct B : ns1::A< /*ns2*/ A, B> {
    typedef ns1::A< /*ns1*/ A, B> Bar;
  };
}

I'm using vscode with g 11.2.0

CodePudding user response:

C classes have an injected class name available in class scope, which refers to the class itself. For example, for ns1::A<T, U>, the name A refers to ns1::A<T, U> in class scope .

This injected class name can also be accessed in derived classes, so in ns2::B, the name A refers to the base class which is ns1::A<ns2::A, ns2::B>.

The injected class name behaves like a member typedef, so it takes precedence over namespace-scope names when in class scope. To refer to a namespace-scope type you'd need to qualify it.

Within ns2::A, you get different behavior: here the base class has an injected class name A referring to ns1::A<int, ns2::A> (similarly to before), but ns2::A also has an injected class name A referring to itself. The derived class's names take precedence so A refers to ns2::A here.

CodePudding user response:

From unqualified names:

For a name used anywhere in class definition (including base class specifiers and nested class definitions), except inside a member function body, a default argument of a member function, exception specification of a member function, or default member initializer, where the member may belong to a nested class whose definition is in the body of the enclosing class, the following scopes are searched:

  • the body of the class in which the name is used until the point of use

(emphasis mine)

Now let us apply this to the two cases.

Case 1

Here we consider:

namespace ns2 {
//-------------------v---------->#1  
  struct B : ns1::A< A, B> {
//------------------v----------->#2
    typedef ns1::A< A, B> Bar;
  };
}

In this case, the unqualified name A at point #1 is looked up and according to bullet point 1 quoted above, it refers to ns2::A.

Now, the base class of B is ns1::A< ns2::A, ns2::B> and this base class has an injected-class name A which refers to the current instantiation ns1::A< ns2::A, ns2::B>. Moreover, it is as-if this base class has a public member named A referring to ns1::A< ns2::A, ns2::B>.

This means that the derived class B behaves as-if it has an inherited member named A from the base class.

Now, the A at point #2 is looked up and since this derived class B has inherited a member named A(same as ns1::A) from the base class, the bullet point 1 founds this ns1::A(which itself refers to ns1::A< ns2::A, ns2::B>). This also explains why in your posted image the type of A at point #2 is ns1::A< ns2::A, ns2::B>.

Case 2

Here we consider:

namespace ns2 {
//-------------------------------v------> #1
  struct /*ns2*/ A : ns1::A<int, A> {
//----------------------v---------------> #2
    typedef ns1::A<int, A> Foo;
  };
 
}

At point #1, according to bullet point 1, A is refers to ns2::A just like in case 1 discussed before.

But this time the inherited member named A from base class ns1::A<int, ns2::A> is hidden by a member with the same name. This is because the class ns2::A itself has an injected-class name A so it is as-if it has a member named A which will hide the inherited member with the same name from the base.

This in turn means that the A at point #2 refers to ns2::A(unlike in case 1).

  • Related