С++ переопределяет частный чистый виртуальный метод как открытый

Почему это происходит?

http://coliru.stacked-crooked.com/a/e1376beff0c157a1

class Base{
private:
    virtual void do_run() = 0;
public:
    void run(){
        do_run();
    }
};

class A : public Base {
public:
    // uplift ??
    virtual void do_run() override {}
};


int main()
{
    A a;
    a.do_run();
}

Почему я могу переопределить виртуальный метод PRIVATE как открытый?

Ответ 1

Согласно https://en.cppreference.com/w/cpp/language/virtual#In_detail, перезапись базовой virtual функции-члена заботится только о имени функции, параметрах, const/volatile-ness и ref qualifier. Он не заботится о типе возврата, модификаторе доступа или других вещах, о которых вы могли бы подумать.

Связанная ссылка также специально отмечает, что:

База :: vf не должна быть видимой (может быть объявлена частной или унаследованной с использованием частного наследования), которая будет переопределена.

Ничто из того, что я могу найти, явно дает разрешение на это, но правила переопределения не мешают этому. Это разрешено в силу virtual функций и функций, которые переопределяют существующие и не запрещают этот случай.

Если вы спрашиваете, почему это так, как язык, вам, возможно, придется обратиться в комитет по стандартизации.

Ответ 2

Это поведение предназначено. Если метод является виртуальным, то он должен быть настраиваемым производными классами, независимо от модификатора доступа.

См. Здесь

Ответ 3

Почему я могу переопределить виртуальный метод PRIVATE как общедоступный???

Потому что вы смотрите, что базовый метод является приватным с неправильным углом. B::do_run - это частные средства, "только члены и друзья этого класса могут его использовать". Чтобы запретить производные классы от переопределения, нам нужен отдельный спецификатор, но мы можем просто сделать его не virtual. Класс A с другой стороны позволяет кому-либо позвонить A::do_run() и это зависит от дизайнера класса A Таким образом, нет никакого поднятия, как вы видите.

Ответ 4

Обратите внимание, что эта реализация не изменяет способ доступа к базовому классу и конструкцию:

Base& b = a;
b.do_run();

не будет работать.

Я помню, что за этим объясняется некоторое обоснование, описанное более подробно в "Эффективном C++" Скотта Мейерса. Но ключевой практической особенностью является возможность использовать такую гибкость в обратном направлении, чтобы переопределить члены публичного базового класса с частными функциями в производном классе, заставляя клиента использовать базовый класс в качестве интерфейса и не испытывать соблазна напрямую использовать который должен оставаться скрытой реализацией.

Ответ 5

Если намерение состоит в том, чтобы написать частный код для базового класса и предотвратить возможность его переопределения, реализовать частную функцию в базовом классе и объявить его final, иначе: когда кто-то будет использовать частные виртуальные машины? Часто задаваемые вопросы по ISOCPP.ORG