使用声明作为覆盖

ixS*_*Sci 5 c++ overriding multiple-inheritance using-declaration language-lawyer

我们在标准中有以下简单(并稍加修改以添加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;
}
Run Code Online (Sandbox Code Playgroud)

从中我们可以得出一个using A::f不存在重写的结论.但标准中的措辞是什么意思呢?以下是C++ 17草案([class.virtual] p2)中最终覆盖的措辞:

<...>类对象S的虚拟成员函数C :: vf是最终覆盖,除非其中S是基类子对象(如果有)的最派生类(4.5)声明或继承覆盖的另一个成员函数vf.在派生类,如果一个基类子对象的虚拟成员函数具有一个以上的最终超控器程序是非法的构造.

我无法找到"覆盖"实际意味着什么.如果没有定义并且我们将任何声明视为重载,那么我们应该将using声明视为重载,因为[namespace.udecl] p2说:

每个using声明都是声明和成员声明,​​因此可以在类定义中使用.

我理解标准使用声明的意图是不引入覆盖,但有人能指出我在Standardese中的实际引用吗?这是第一部分,现在是第二部分


请考虑以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

如果using声明没有引入覆盖,那么我们有2个最终覆盖D,因此 - 由于的未定义行为

在派生类中,如果基类子对象的虚拟成员函数具有多个最终覆盖,则程序是格式错误的.

对?

Sto*_*ica 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 ;
Run Code Online (Sandbox Code Playgroud)

并且在某种程度上是语义上的.由于以下段落详细说明了如何从基类引入成员函数的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(该示例中的继承不是虚拟的).而每一个在拥有自己的最终置换器BC分别.所以程序不是格式错误,无论是否声明了另一个覆盖者D.

您关注的段落适用于虚拟继承的情况.如果BC拥有一个虚拟A基础,并D从两者继承而没有覆盖print,那么该程序将是不正确的.并且using C::print由于上述原因,使用声明不会使其形成良好.