作者:AA一缕阳光 | 来源:互联网 | 2022-12-01 10:46
我们在标准中有以下简单(并稍加修改以添加main
和输出)示例:
struct A {
virtual void f()
{
cout <<"A\n";
}
};
struct B : virtual A {
virtual void f()
{
cout <<"B\n";
}
};
struct C : B, virtual A {
using A::f;
};
int main()
{
C c;
c.f(); // calls B?::?f, the final overrider
c.C::f();
return 0;
}
从中我们可以得出一个using A::f
不存在重写的结论.但标准中的措辞是什么意思呢?以下是C++ 17草案([class.virtual] p2)中最终覆盖的措辞:
<...>类对象S的虚拟成员函数C :: vf是最终覆盖,除非其中S是基类子对象(如果有)的最派生类(4.5)声明或继承覆盖的另一个成员函数vf.在派生类,如果一个基类子对象的虚拟成员函数具有一个以上的最终超控器程序是非法的构造.
我无法找到"覆盖"实际意味着什么.如果没有定义并且我们将任何声明视为重载,那么我们应该将using声明视为重载,因为[namespace.udecl] p2说:
每个using声明都是声明和成员声明,因此可以在类定义中使用.
我理解标准使用声明的意图是不引入覆盖,但有人能指出我在Standardese中的实际引用吗?这是第一部分,现在是第二部分
请考虑以下代码:
#include
#include
using std::cout;
class A {
public:
virtual void print() const {
cout <<"from A" <
如果using声明没有引入覆盖,那么我们有2个最终覆盖D
,因此 - 由于的未定义行为
在派生类中,如果基类子对象的虚拟成员函数具有多个最终覆盖,则程序是格式错误的.
对?
1> StoryTeller ..:
就声明性区域而言,使用声明虽然确实是声明,但它不是函数声明.我们可以看到它用语法指定:
[dcl.dcl]
1声明通常指定如何解释名称.声明具有形式
declaration:
block-declaration
nodeclspec-function-declaration
function-definition
template-declaration
deduction-guide
explicit-instantiation
explicit-specialization
linkage-specification
namespace-definition
empty-declaration
attribute-declaration
block-declaration:
simple-declaration
asm-definition
namespace-alias-definition
using-declaration
using-directive
static_assert-declaration
alias-declaration
opaque-enum-declaration
nodeclspec-function-declaration:
attribute-specifier-seq declarator ;
并且在某种程度上是语义上的.由于以下段落详细说明了如何从基类引入成员函数的using声明与派生类中的成员函数声明不同.
[namespace.udecl]
15当using-declarator将基类的声明带入派生类时,派生类中的成员函数和成员函数模板覆盖和/或隐藏具有相同名称的成员函数和成员函数模板,parameter-type-list,cv - 基类中的-qualification和ref-qualifier(如果有)(而不是冲突).这些隐藏或重写的声明被排除在using-declarator引入的声明集之外.
16出于重载解析的目的,using-declaration引入派生类的函数被视为派生类的成员.特别是,隐式this参数应被视为指向派生类而不是基类的指针.这对函数的类型没有影响,并且在所有其他方面,函数仍然是基类的成员.
考虑到这一点,如果考虑到第一段的开头,你引用:
[class.virtual]
2如果虚拟成员函数vf
在类Base和Derived中直接或间接派生的类中声明,则具有相同名称的成员函数vf,parameter-type-list,cv-qualification和ref-qualifier(或如果没有
Base?::?vf
声明,那么Derived?::?vf
它也是虚拟的(无论是否如此声明)并且它会覆盖Base?::?vf
.为方便起见,我们说任何虚函数都会覆盖自身.
我们可以看到它是一个虚函数声明,可以为基类中的虚函数引入一个覆盖.由于使用声明不是函数声明,因此它不符合条件.
目前的措辞部分来自CWG缺陷608.其目的是澄清该报告中有问题的解释,并将声明与虚函数覆盖的概念分离.
关于你的第二个问题,引用中重要的是"基类子对象 ".您的代码示例中有两个 A
子对象D
(该示例中的继承不是虚拟的).而每一个在拥有自己的最终置换器B
和C
分别.所以程序不是格式错误,无论是否声明了另一个覆盖者D
.
您关注的段落适用于虚拟继承的情况.如果B
并C
拥有一个虚拟A
基础,并D
从两者继承而没有覆盖print
,那么该程序将是不正确的.并且using C::print
由于上述原因,使用声明不会使其形成良好.