使(虚拟)函数在大多数派生类中无法访问中间基类中可访问,定义良好?



请考虑以下代码:

#include <iostream>
struct a {
virtual void f() = 0;
};
struct b: a {
private:
void f() override {
std::cout << "b::f()n";
}
};
struct c: b {
public:
using a::f;
};
int main() {
::c c;
c.f();
}

这编译并按预期与g++clangmsvc,即打印b::f()

。但是,如果我用这个替换ab

struct a {
void f() { }
};
struct b: a {
private:
using a::f;
};

。代码不再使用gcc编译,而是使用clangclang-clmsvc编译 fine(感谢 StoryTeller 和 Adrian Mole(。我收到以下错误(在cusing a::f;行上(:

错误:"void a::f()"在此上下文中无法访问

在这些情况下,我在标准中找不到关于using a::f;(c(行为的明确点,那么标准是否明确定义了上述内容?

注意:我不是在谈论在类的public范围内引入某些内容(例如b::f受到保护,则cusing b::f(,而是真正使顶级类的成员在派生最多的类中可访问,因为这些成员在中间基类中无法访问。

我相信 GCC 拒绝修改后的代码是错误的。

[命名空间.udecl]

1 使用声明中的每个使用声明符都引入了一组 声明进入声明性区域的声明,其中 此时将显示使用声明。由 使用声明符是通过执行限定名称查找找到的 ([basic.lookup.qual], [class.member.lookup]( 中的名称 使用声明符,不包括如上所述隐藏的函数 下面。

3 在用作成员声明的使用声明中,每个 使用声明符的嵌套名称说明符应命名基类 正在定义的类。如果 using 声明符命名构造函数, 其嵌套名称说明符应命名该类的直接基类 正在定义。

因此,首先我要指出,第3段区分了基础和直接基础。因此,我们可以在 using 声明中命名a::f。其次,根据第1款,姓名查询按预期进行

[等级评定]

1 如果限定 id 的嵌套名称说明符指定了 类,在查找嵌套名称说明符后指定的名称 在类的作用域 ([class.member.lookup]( 中,除了 下面列出了案例。该名称应代表一个或多个成员 该类或其基类之一(子句 [class.派生](。

[class.member.lookup]

1 成员名称查找确定名称的含义 (id-expression(在类作用域中。名称查找可能会导致 歧义,在这种情况下,程序格式不正确。对于一个 id-expression,名称查找从这个类范围开始;对于一个 限定 ID,名称查找从 嵌套名称说明符。名称查找在访问控制之前进行。

因此,a::f只能在a或其自己的基类范围内查找。根本不应该在b中查找它.因此,我认为,在进行限定名称查找时,b中名称f的可访问性不应影响a中名称f的可访问性。

af是公开的。因此,可以在任何可能命名a声明性区域中由限定 id 命名。这包括c.因此,using 声明对基类的有效成员使用有效名称。这在该声明性区域中是可访问的。因此,它是有效的。

作为另一个数据点,GCC在其他用途中a::f的可访问性没有问题。例如,GCC 允许形成指向c范围内a::f的成员的指针。

struct c: b {
public:
c() {
[[maybe_unused]] auto f = &a::f;
};
};

因此,由于b,它显然不认为该名称在所有上下文a::f无法访问。

最新更新