使(虚拟)函数在中间基 class 中不可访问,在大多数派生 class 中可访问,定义明确吗?
Making (virtual) functions made innaccessible in intermediate base class accessible in most-derived class, well-defined?
考虑以下代码:
#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
编译罚款(感谢 StoryTeller 和 Adrian Mole)。我收到以下错误(在 c
中的 using a::f;
行):
error: 'void a::f()
' is inaccessible within this context
我在标准中找不到关于 using a::f;
(在 c
中)在这些情况下的行为的明确点,那么标准是否明确定义了上述内容?
注意: 我不是在谈论在 class 的 public
范围内引入某些东西(比如 using b::f
在 c
if b::f
was protected),但真正让来自顶级 class 的成员在最派生的 class 中可访问,因为这些成员在中间基础 [=48] 中不可访问=].
我认为 GCC 拒绝修改后的代码是错误的。
[namespace.udecl]
1 Each using-declarator in a using-declaration introduces a set
of declarations into the declarative region in which the
using-declaration appears. The set of declarations introduced by the
using-declarator is found by performing qualified name lookup
([basic.lookup.qual], [class.member.lookup]) for the name in the
using-declarator, excluding functions that are hidden as described
below.
3 In a using-declaration used as a member-declaration, each
using-declarator's nested-name-specifier shall name a base class of
the class being defined. If a using-declarator names a constructor,
its nested-name-specifier shall name a direct base class of the class
being defined.
首先我要注意第 3 段区分了基数和直接基数。因此我们可以在 using 声明中命名 a::f
。其次,根据第 1 段,名称查找按预期进行
[class.qual]
1 If the nested-name-specifier of a qualified-id nominates a
class, the name specified after the nested-name-specifier is looked up
in the scope of the class ([class.member.lookup]), except for the
cases listed below. The name shall represent one or more members of
that class or of one of its base classes (Clause [class.derived]).
[class.member.lookup]
1 Member name lookup determines the meaning of a name
(id-expression) in a class scope. Name lookup can result in an
ambiguity, in which case the program is ill-formed. For an
id-expression, name lookup begins in the class scope of this; for a
qualified-id, name lookup begins in the scope of the
nested-name-specifier. Name lookup takes place before access control.
所以 a::f
只能在 a
的范围内或者它自己的基础 class 中查找。根本不应该在 b
中查找它。因此,我认为在进行限定名称查找时,b
中名称 f
的可访问性不应影响 a
中名称 f
的可访问性。
在a
中,f
是public。因此可以在可以命名 a
的任何声明区域中通过限定 ID 命名。其中包括 c
。因此 using 声明为基 class 的有效成员使用了有效名称。在该声明区域中可以访问。因此它是有效的。
作为另一个数据点,GCC在其他用途上a::f
的可访问性没有问题。 For example,GCC 允许在 c
.
范围内形成指向 a::f
成员的指针
struct c: b {
public:
c() {
[[maybe_unused]] auto f = &a::f;
};
};
所以它显然不认为名称 a::f
由于 b
在所有上下文中都不可访问。
考虑以下代码:
#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
编译罚款(感谢 StoryTeller 和 Adrian Mole)。我收到以下错误(在 c
中的 using a::f;
行):
error: '
void a::f()
' is inaccessible within this context
我在标准中找不到关于 using a::f;
(在 c
中)在这些情况下的行为的明确点,那么标准是否明确定义了上述内容?
注意: 我不是在谈论在 class 的 public
范围内引入某些东西(比如 using b::f
在 c
if b::f
was protected),但真正让来自顶级 class 的成员在最派生的 class 中可访问,因为这些成员在中间基础 [=48] 中不可访问=].
我认为 GCC 拒绝修改后的代码是错误的。
[namespace.udecl]
1 Each using-declarator in a using-declaration introduces a set of declarations into the declarative region in which the using-declaration appears. The set of declarations introduced by the using-declarator is found by performing qualified name lookup ([basic.lookup.qual], [class.member.lookup]) for the name in the using-declarator, excluding functions that are hidden as described below.
3 In a using-declaration used as a member-declaration, each using-declarator's nested-name-specifier shall name a base class of the class being defined. If a using-declarator names a constructor, its nested-name-specifier shall name a direct base class of the class being defined.
首先我要注意第 3 段区分了基数和直接基数。因此我们可以在 using 声明中命名 a::f
。其次,根据第 1 段,名称查找按预期进行
[class.qual]
1 If the nested-name-specifier of a qualified-id nominates a class, the name specified after the nested-name-specifier is looked up in the scope of the class ([class.member.lookup]), except for the cases listed below. The name shall represent one or more members of that class or of one of its base classes (Clause [class.derived]).
[class.member.lookup]
1 Member name lookup determines the meaning of a name (id-expression) in a class scope. Name lookup can result in an ambiguity, in which case the program is ill-formed. For an id-expression, name lookup begins in the class scope of this; for a qualified-id, name lookup begins in the scope of the nested-name-specifier. Name lookup takes place before access control.
所以 a::f
只能在 a
的范围内或者它自己的基础 class 中查找。根本不应该在 b
中查找它。因此,我认为在进行限定名称查找时,b
中名称 f
的可访问性不应影响 a
中名称 f
的可访问性。
在a
中,f
是public。因此可以在可以命名 a
的任何声明区域中通过限定 ID 命名。其中包括 c
。因此 using 声明为基 class 的有效成员使用了有效名称。在该声明区域中可以访问。因此它是有效的。
作为另一个数据点,GCC在其他用途上a::f
的可访问性没有问题。 For example,GCC 允许在 c
.
a::f
成员的指针
struct c: b {
public:
c() {
[[maybe_unused]] auto f = &a::f;
};
};
所以它显然不认为名称 a::f
由于 b
在所有上下文中都不可访问。