Name lookup of qualified base class

246 views Asked by At

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::S specifically
  • Are (1) and (2) both required to work?
  • Why does S s; inside f() refer to D::S and not ::S ?
1

There are 1 answers

0
Jonathan Wakely On BEST ANSWER

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.