«скрытая перегруженная виртуальная функция» с более чем 2 классами - PullRequest
0 голосов
/ 24 ноября 2018

Минимальный пример:

class A {};
class B : public virtual A {};
class C : public virtual B {};

// define two overloading virtual functions
// no inheritance, so no overriding/hiding in this class
struct visitor1
{
    virtual void visit(A& ) {}
    virtual void visit(B& ) {}
    virtual ~visitor1();
};

// covariant types are not allowed for overriding virtuals,
// so the C-visit function would hide A and B, so we add them
// using the "using" keyword
struct visitor2 : public visitor1
{
    using visitor1::visit;
    virtual void visit(C& ) {}
    virtual ~visitor2();
};

// the B-visit is a correct override
// without the use of "using" (or the C-visit) below, the
// compiler warns that the C-visit function from visitor3
// is being overridden
struct visitor3 final : public visitor2
{
    //using visitor2::visit;
    //void visit(C &) override {}
    void visit(B &) override {}
};

Компилятор:

$ clang++ -Wall -Wextra -Weverything -Wno-c++98-compat visitor.cpp 
visitor.cpp:32:14: warning: 'visitor3::visit' hides overloaded virtual function [-Woverloaded-virtual]
        void visit(B &) override {}
             ^
visitor.cpp:20:22: note: hidden overloaded virtual function 'visitor2::visit' declared here: type mismatch at 1st parameter ('C &' vs 'B &')
        virtual void visit(C& ) {}
                     ^

Вопрос (при условии, что закомментированные строки от посетителя 3 остаются прокомментированными):

  • Почему компилятор жалуется, что visitor3::visit(B&) скрывает visitor2::visit(C&)?
  • Почему он не жалуется, что visitor3::visit(B&) скрывает visitor1::visit(A&) (что здесь отличается)?

Дополнительный вопрос : Как добиться, чтобы и visitor2::visit(C&), и visitor1::visit(A&) не были скрыты в visitor3?using visitor2::visit; в visitor3 достаточно?

Ответы [ 2 ]

0 голосов
/ 24 ноября 2018

«Сокрытие» - это сокращение от того, что действительно происходит.Правило простое (): при поиске имени компилятор запускается в текущей области видимости;если он найдет это имя, он готов.Если он не находит его, он перемещается в следующую объемную область.Повторяйте, пока не закончите.

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

Обратите внимание, что поиск имени ищет только имя;это не зависит от того, переопределяет ли найденное имя имя в базовом классе, а также от того, существует ли в текущей области более одной функции с одинаковым именем (т. е. имя перегружено).Как только имя найдено, поиск заканчивается.Все определения с таким именем в этой области участвуют в перегрузке.

Итак, void visit(B&) в visitor3 скрывает все остальные определения из visit ввсе базовые классы.Имя определено в visitor3, поэтому компилятор больше нигде не выглядит.

0 голосов
/ 24 ноября 2018

Объявление void visit(B&) или любое объявление члена с именем visit (это мог быть элемент данных int visit;) в visitor3 скрывает любой член родителя с именем visit.

Здесь предупреждение компилятора - больше вкусностей.Он распознал распространенную схему ошибок, которая заключается в переопределении базовой функции виртуального члена без учета перегрузок.Но он не обнаруживает все скрытые имена или не предупреждает их всех.Как бы то ни было, все visit из visitor1 и visitor2 скрыты.

Дополнительный ответ : объявление использования, которое именует членов базового класса, является объявлением внутри производного классавсе члены в базовом классе, которые имеют это имя и являются видимыми.Таким образом, это эффективно включает членов, которые также объявлены с помощью объявления использования в базовом классе.

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

...