我们在标准中有以下简单(并稍加修改以添加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-declaration都是一个声明和一个成员声明,因此可以在类定义中使用。
我了解标准的意图,即使用声明不引入替代程序,但是有人可以指出实际使用标准语的引号吗?这是第一部分,现在到第二部分
考虑以下代码:
#include <iostream>
#include <string>
using std::cout;
class A {
public:
virtual void print() const {
cout << "from A" << std::endl;
}
};
class B: public A {
public:
void print() const override {
cout << "from B" << std::endl;
}
};
class C: public A {
public:
void print() const override {
cout << "from C" << std::endl;
}
};
class D: public B, public C {
public:
using C::print;
};
int main()
{
D d{};
d.print();
return 0;
}
如果using声明未引入覆盖程序,则D
中将有2个最终覆盖程序,因此-由于
在派生类中,如果是虚拟成员 基类子对象的功能具有多个最终重写器 该程序格式不正确。
对吗?
答案 0 :(得分:6)
使用声明,尽管实际上是涉及声明性区域的声明,但不是功能声明。我们可以看到它是语法指定的:
[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声明引入到派生类中被视为 尽管他们是派生类的成员。特别是 隐式地将此参数视为是指向的指针 派生类而不是基类。这对 函数的类型,以及在所有其他方面,函数 仍然是基类的成员。
请牢记这一点,如果您考虑到第一段的开头,请引用:
[class.virtual]
2如果在类Base中声明了虚拟成员函数
vf
在直接或间接从Base派生的类中, 具有相同名称,参数类型列表的成员函数vf, cv限定词和ref限定词(或不存在) 声明Base::vf
,然后Derived::vf
也是虚拟的 (无论是否声明),它都将覆盖Base::vf
。对于 方便起见,我们说任何虚函数都可以覆盖自身。
我们可以看到它是一个虚拟的 function声明,它可以在基类中为虚拟函数引入重写器。而且,由于using声明不是函数声明,因此不符合条件。
当前措词部分来自CWG Defect 608。目的在于澄清该报告中的问题解释,并使用声明与虚函数重写器的概念进行解耦。
关于第二个问题,在该引号中要注意的重点是“基类 subobject 。您的代码示例在A
中有两个 D
子对象(该示例中的继承不是虚拟的)。每个人在B
和C
中都有自己的最终替代项。因此,无论是否在D
中声明另一个替代程序,该程序都不会格式错误。
您关注的段落适用于虚拟继承的情况。如果B
和C
具有虚拟的A
基址,并且D
从两者继承而没有覆盖print
,则程序would be ill-formed。同样由于上述原因,using C::print
这样的using声明也无法使其格式正确。