Consider this code:
#include <iostream>
namespace D
{
struct S { S(){std::cout << "D::S\n";} };
}
struct S { S(){std::cout << "S\n";} };
struct X: D::S
{
X(): S() {} // (1)
// X(): D::S() {} // (2)
void f() { S s; }
};
int main() { X x; x.f(); }
Output from g++ is:
D::S
D::S
My questions are:
- How does (1) work - I would have though that the name of the base class is
D::Sspecifically - Are (1) and (2) both required to work?
- Why does
S s;insidef()refer toD::Sand not::S?
Within the body of the class
D::Sthe nameSrefers to itself, obviously. This is called the "injected class name". You can think of it as though there is a public member typedef inD::Swith its own name,S.Xderives fromD::S, because you said so in the base class list ofX.A derived class has access to the names declared in a base class, so name lookup in
Xfirst looks at its own members and its base class' members, then looks for names in the enclosing scope outsideX. Because the injected class nameSis a member ofD::S, it gets found inX, that's why (1) works. The type::Sis not found because name lookup finds the injected class name and never looks in the enclosing scope (if it did find::Sthe code wouldn't compile, because::Sis not a base class ofX).As an analogy consider this example using a member typedef declared in
D::S:This works because the name
AnotherNameis found in the base class, and is a synonym for the type of the base class,D::S. The injected class name works similarly, except that the name that gets injected is the class' own name,S, not some other name likeAnotherName.Yes.
(2) works because
D::Sis the fully-qualified named ofSso it refers to the same type, but using its "full name" that non-members must use to refer to the type.Because like the constructor,
f()is a member ofXso name lookup looks inside the scope ofX(and its base classes) first, and so finds the injected class name. It never see the type::Sat global scope because it finds the nameSas a member of the base class and stops looking.