0

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 ?
M.M
  • 138,810
  • 21
  • 208
  • 365
  • 2
    `S` in `X` is the *injected class name* referring to `D::S`. – David G Jun 16 '15 at 23:08
  • [class.base.init]/3 guarantees that both (1) and (2) would work. "A *mem-initializer-list* can initialize a base class using any *class-or-decltype* that denotes that base class type." – Brian Bi Jun 16 '15 at 23:14
  • @0x499602D2 I didn't realize (until reading Jonathan's answer) that it is the injected class name *injected into D::S* (not into X) and so it is in X's scope because items from X's base class are in X's scope. – M.M Jun 16 '15 at 23:21

1 Answers1

5

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.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521