使用声明作为覆盖

Using declaration as overrider

我们在标准中有以下简单(并略微修改以添加 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) 中最终覆盖程序的措辞:

<...> A virtual member function C::vf of a class object S is a final overrider unless the most derived class (4.5) of which S is a base class subobject (if any) declares or inherits another member function that overrides vf. In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed.

而且我无法找到 "overrides" 的真正含义。如果它未定义并且我们将任何声明视为覆盖程序,那么我们应该将 using 声明视为覆盖程序,因为 [namespace.udecl]p2 说:

Every using-declaration is a declaration and a member-declaration and can therefore be used in a class definition.

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


考虑以下代码:

#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 个最终覆盖器,因此 - 由于

的未定义行为

In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed.

对吗?

using 声明,虽然就声明区域而言确实是声明,但不是 函数声明。我们可以看到它在语法上是指定的:

[dcl.dcl]

1 Declarations generally specify how names are to be interpreted. Declarations have the form

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 ;

并且在某种程度上是语义上的。由于以下段落详细说明了从基 class 引入成员函数的 using 声明与派生 class.

中的成员函数声明有何不同

[namespace.udecl]

15 When a using-declarator brings declarations from a base class into a derived class, member functions and member function templates in the derived class override and/or hide member functions and member function templates with the same name, parameter-type-list, cv-qualification, and ref-qualifier (if any) in a base class (rather than conflicting). Such hidden or overridden declarations are excluded from the set of declarations introduced by the using-declarator.

16 For the purpose of overload resolution, the functions that are introduced by a using-declaration into a derived class are treated as though they were members of the derived class. In particular, the implicit this parameter shall be treated as if it were a pointer to the derived class rather than to the base class. This has no effect on the type of the function, and in all other respects the function remains a member of the base class.

考虑到这一点,如果考虑到您引用的第一段的开头:

[class.virtual]

2 If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list, cv-qualification, and ref-qualifier (or absence of same) as Base​::​vf is declared, then Derived​::​vf is also virtual (whether or not it is so declared) and it overrides Base​::​vf. For convenience we say that any virtual function overrides itself.

我们可以看到它是一个虚函数声明,它可以在基class中为虚函数引入覆盖。由于 using 声明不是函数声明,因此它不合格。

当前的措辞部分来自CWG Defect 608。它旨在澄清该报告中有问题的解释,并将使用声明与虚函数覆盖的概念分离。


关于你的第二个问题,引述中需要注意的重要一点是 "of a base class subobject"。您的代码示例在 D 中有 two A 个子对象(该示例中的继承不是虚拟的)。每个人都有自己的最终覆盖,分别在 BC 中。因此该程序不是病式的,无论是否在 D.

中声明了另一个覆盖程序

您关注的段落适用于虚拟继承的情况。如果 BC 有一个虚拟的 A 基础,并且 D 从两者继承而不覆盖 print,程序 would be ill-formed。由于上述原因,像 using C::print 这样的 using 声明不会使其格式正确。