Общедоступный виртуальный метод переопределен как частный. Нарушение принципов обобщения/специализации/Лисков?

Как и в случае с членом закрытой функции, вызываемым вне класса, можно написать следующий код:

#include <iostream>

class A {
public:
  virtual void f() { std::cout << "A::f()"; }
};

class B : public A {
private:
  void f() override { std::cout << "B::f()"; }
};

void g(A &g) { g.f(); }

int main() {
  A a;
  g(a);
  a.f();
  B b;
  g(b);
  b.f(); // compilation failure
}

Разумеется, компилятор отказывается компилировать последнюю строку, потому что статический анализ кода показывает, что B::f() определено, но приватно.

Что меня серьезно беспокоит, так это отношение к концептуальному обобщению/специализации. Обычно считается, что вы должны иметь возможность манипулировать экземпляром подтипа, по крайней мере, так же, как вы манипулируете экземпляром супертипа.

На этом основан принцип подстановки Лисков. В данном примере это соблюдается, когда g() вызывается либо с аргументом типа A, либо с аргументом типа B. Но последняя строка не принимается, и кажется, что в таком случае принцип подстановки в чем-то нарушается (считайте вызов по имени, как в макроопределении #define h(x) (x.f())).

Даже если считать, что принцип Лискова не нарушается (макросы не являются реальной частью языка, так что ок), тот факт, что последняя строка выдает ошибку времени компиляции, как минимум означает, что объекты типа B не могут можно манипулировать, как A. Так что B не является специализацией A, даже если производным является public.

Таким образом, в C++ использование порождения public не гарантирует, что вы эффективно реализуете специализацию. Вам нужно учитывать больше свойств кода, чтобы быть уверенным, что у вас «правильная» специализация.

Я ошибаюсь ? Кто-нибудь может дать мне обоснование для этого? Мне нужен хороший семантический аргумент, я имею в виду, по крайней мере, что-то гораздо более сложное, чем аргументы Страуструпа, например, «С++ старается не ограничивать вас, вы можете использовать его, если хотите, и не можете, если не хотите». Я думаю, что язык должен быть основан на разумной модели, а не на огромном списке возможных уловок.


person Jean-Baptiste Yunès    schedule 22.01.2015    source источник
comment
Я не понимаю, как вы можете переопределить общедоступный виртуальный метод приватным.   -  person Matthew    schedule 23.01.2015
comment
Это возможно в C++, поэтому вопрос не в том, «как?», а в том, почему это возможно?   -  person Jean-Baptiste Yunès    schedule 23.01.2015
comment
Я вполне уверен, что в соответствии со стандартом C++ доступность просто не является частью сигнатуры функции-члена. Это ответы на вопрос, почему это разрешено. Однако я не уверен, что могу ответить, должен ли стандарт разрешать это.   -  person mbgda    schedule 23.01.2015
comment
Хорошо, но это технический аргумент. Но я хочу знать почему, потому что это очень странно...   -  person Jean-Baptiste Yunès    schedule 23.01.2015
comment
Я просто объяснил почему. Переопределение работы по сигнатуре и доступности не меняет сигнатуру функции.   -  person mbgda    schedule 23.01.2015
comment
Нет, вы объяснили, как это работает (я знаю, как), а не почему это возможно? Почему дизайнеры С++ разрешили это, поскольку это в основном бессмысленно (для меня)? Это строго запрещено на любом другом языке ООП, который я знаю.   -  person Jean-Baptiste Yunès    schedule 23.01.2015
comment
Я не уверен, почему это было разработано таким образом. Стоит отметить, что называть C++ языком ООП вводит в заблуждение. Это мультипарадигмальный язык, поэтому он не на 100% разработан в соответствии с парадигмой ООП. Кроме того, я полагаю, что могу придумать один вариант использования для этого. Вы хотите предоставить реализацию «f» внутри «B», но вы не хотите, чтобы кто-либо, кто знает, что у них есть «B», напрямую вызывал его. Вы хотите заставить их всегда вызывать его через дескриптор базового класса по любой причине. Очевидно, что использование NVI было бы лучшим способом приблизиться к этому.   -  person mbgda    schedule 23.01.2015
comment
Хорошо, вы имеете в виду использование фабрики, но если вы не хотите, чтобы другие знали, что B существует, то не нужно ничего в ней защищать.   -  person Jean-Baptiste Yunès    schedule 23.01.2015
comment
Да, это не лучший пример. Может быть, нам нужно, чтобы Страуструп ответил за это :)   -  person mbgda    schedule 23.01.2015