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:
D::S
specificallyS s;
inside f()
refer to D::S
and not ::S
?Within the body of the class D::S
the name S
refers to itself, obviously. This is called the "injected class name". You can think of it as though there is a public member typedef in D::S
with its own name, S
.
- How does (1) work - I would have though that the name of the base class is D::S specifically
X
derives from D::S
, because you said so in the base class list of X
.
A derived class has access to the names declared in a base class, so name lookup in X
first looks at its own members and its base class' members, then looks for names in the enclosing scope outside X
. Because the injected class name S
is a member of D::S
, it gets found in X
, that's why (1) works. The type ::S
is not found because name lookup finds the injected class name and never looks in the enclosing scope (if it did find ::S
the code wouldn't compile, because ::S
is not a base class of X
).
As an analogy consider this example using a member typedef declared in D::S
:
namespace D {
struct S {
struct S { S(){std::cout << "D::S\n";} };
typedef S AnotherName;
};
}
struct X : D::S {
X() : AnotherName() { }
};
This works because the name AnotherName
is 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 like AnotherName
.
- Are (1) and (2) both required to work?
Yes.
(2) works because D::S
is the fully-qualified named of S
so it refers to the same type, but using its "full name" that non-members must use to refer to the type.
- Why does S s; inside f() refer to D::S and not ::S ?
Because like the constructor, f()
is a member of X
so name lookup looks inside the scope of X
(and its base classes) first, and so finds the injected class name. It never see the type ::S
at global scope because it finds the name S
as a member of the base class and stops looking.