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).