名称查找合格的基类

M.M*_*M.M 0 c++ inheritance name-lookup

考虑以下代码:

#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(); }
Run Code Online (Sandbox Code Playgroud)

g ++的输出是:

D::S
D::S
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  • (1)如何工作 - 虽然基类的名称是D::S具体的,但我会有
  • (1)和(2)都需要工作吗?
  • 为什么S s;内部f()参考D::S而不是::S

Jon*_*ely 5

在类的主体内,D::S名称S显然是指自己.这称为"注入类名".您可以将其视为D::S具有自己名称的公共成员typedef S.

  • (1)如何工作 - 我会认为基类的名称是D :: S

X派生自D::S,因为你在基类列表中这么说X.

派生类可以访问基类中声明的名称,因此名称查找X首先查看其自己的成员及其基类的成员,然后在外部的封闭范围中查找名称X.因为注入的类名S是其成员D::S,所以它被发现X,这就是为什么(1)有效.::S找不到类型,因为名称查找找到了注入的类名,并且从不在封闭范围内查找(如果它确实发现::S代码不能编译,因为::S它不是基类X).

作为类比,请考虑使用在D::S以下位置声明的成员typedef的此示例:

namespace D {
  struct S {
    struct S { S(){std::cout << "D::S\n";} };
    typedef S AnotherName;
  };
}

struct X : D::S {
  X() : AnotherName() { }
};
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为名称AnotherName可以在基类中找到,并且是基类类型的同义词D::S.注入的类名称的工作方式类似,只是注入的名称是类的自己的名称S,而不是其他名称AnotherName.

  • (1)和(2)都需要工作吗?

是.

(2)作品因为D::S是完全限定的名称S所以它指的是同一类型,但使用其非全员必须使用的"全名"来引用该类型.

  • 为什么S s; 在f()里面是指D :: S而不是:: S?

因为像构造函数一样,f()X名称查找的成员X首先在(及其基类)的范围内查找,因此查找注入的类名.它永远不会::S在全局范围内看到类型,因为它将名称S视为基类的成员并停止查找.