请考虑以下代码:
#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++
、clang
和msvc
,即打印b::f()
。但是,如果我用这个替换a
和b
:
struct a {
void f() { }
};
struct b: a {
private:
using a::f;
};
。代码不再使用gcc
编译,而是使用clang
、clang-cl
和msvc
编译 fine(感谢 StoryTeller 和 Adrian Mole(。我收到以下错误(在c
的using a::f;
行上(:
错误:"
void a::f()
"在此上下文中无法访问
在这些情况下,我在标准中找不到关于using a::f;
(c
(行为的明确点,那么标准是否明确定义了上述内容?
注意:我不是在谈论在类的public
范围内引入某些内容(例如b::f
受到保护,则c
using 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
的可访问性。
在a
,f
是公开的。因此,可以在任何可能命名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
无法访问。