Использование объявления в качестве переопределителя - PullRequest
0 голосов
/ 29 октября 2018

У нас есть следующий простой (и немного измененный, чтобы добавить 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):

<...> Виртуальная функция-член C :: vf объекта класса S является конечной переопределить, если не самый производный класс (4.5), из которых S является базой подобъект класса (если есть) объявляет или наследует другого члена функция, которая переопределяет vf . В производном классе, если виртуальный член функция подобъекта базового класса имеет более одного окончательного переопределения программа некорректна.

И я не смог найти, что на самом деле означают "переопределения". Если оно не определено, и мы рассматриваем любое объявление как переопределитель, то мы должны считать, что использование using переопределено, так как [namespace.udecl] p2 говорит:

Каждое объявление об использовании является объявлением и объявлением члена и поэтому может использоваться в определении класса.

Я понимаю намерение Стандарта использовать декларацию, чтобы не вводить переопределение, но может ли кто-нибудь указать мне фактические цитаты, которые говорят, что в Стандартном? Это первая часть, теперь вторая


Рассмотрим следующий код:

#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;
}

Если объявление использования не вводит переопределение, то у нас есть 2 финальных переопределения в D, следовательно, - неопределенное поведение из-за

В производном классе, если виртуальный член функция подобъекта базового класса имеет более одного окончательного переопределения программа некорректна.

правый

1 Ответ

0 голосов
/ 29 октября 2018

Объявление использования, хотя действительно объявление в отношении декларативных областей, не является объявлением функции . Мы можем видеть это указано грамматически:

[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 Когда использование-декларатор приносит объявления из базового класса в производный класс, функции-члены и шаблоны функций-членов в производном классе переопределяют и / или скрывают функции-члены шаблоны функций с тем же именем, параметр-тип-список, cv-квалификация и ref-квалификатор (если есть) в базовом классе (скорее чем противоречивый). Такие скрытые или переопределенные объявления исключаются из набора объявлений, введенных оператором using.

16 Для разрешения перегрузки функции, которые введенные объявлением использования в производный класс рассматриваются как хотя они были членами производного класса. В частности, неявный этот параметр должен рассматриваться как указатель на производный класс, а не базовый класс. Это не влияет на тип функции и во всех других отношениях функция остается членом базового класса.

Имея это в виду, если принять во внимание начало первого абзаца, вы цитируете:

[class.virtual]

2 Если функция виртуального члена vf объявлена ​​в классе Base и в классе Derived, полученном прямо или косвенно из Base, функция-член vf с тем же именем, параметр-тип-список, cv-квалификация и ref-квалификатор (или его отсутствие) как Base​::​vf объявлено, тогда Derived​::​vf также является виртуальным (независимо от того, объявлено оно или нет), и оно переопределяет Base​::​vf. За Для удобства мы говорим, что любая виртуальная функция переопределяет себя.

Мы можем видеть, что это виртуальное объявление функции , которое может ввести переопределение для виртуальной функции в базовом классе. А поскольку объявление using не является объявлением функции, оно не подходит.

Нынешняя формулировка частично относится к CWG Defect 608 . Он призван прояснить проблемную интерпретацию в этом отчете и отделить использование объявлений от понятия переопределения виртуальных функций.


Что касается вашего второго вопроса, важно отметить в этой цитате "базового класса подобъекта " . В вашем примере кода есть два A подобъекта в D (наследование в этом примере не виртуальное). И у каждого есть свой окончательный переопределение в B и C соответственно. Таким образом, программа не является некорректной, с или без переопределения, объявленного в D.

Пункт, который вас интересует, относится к случаю виртуального наследования. Если бы B и C имели виртуальную базу A, а D унаследовали от обоих без переопределения print, программа была бы плохо сформирована . А объявление об использовании, такое как using C::print, не будет правильно сформировано, опять же по причинам, указанным выше.

...