Я пытаюсь понять, как C++ обрабатывает указатели на функции-члены и разрешение перегрузки в контексте наследования. У меня есть базовый класс с чистой виртуальной функцией и производный класс. Я перегрузил функцию doSomething, чтобы принимать указатели на функции-члены как из базового, так и из производных классов. Вот код:
#include <iostream>
class Base {
public:
virtual void foo() = 0;
};
class Derived: public Base {};
void doSomething(void (Base::*func)(void)) {
std::cout << "Base overload\n";
}
void doSomething(void (Derived::*func)(void)) {
std::cout << "Derived overload\n";
}
int main(void) {
doSomething(&Derived::foo);
}
Когда я запускаю этот код, результат:
Base overload
Я ожидал, что будет вызвана «Производная перегрузка», потому что я передаю адрес Derived::foo. Может ли кто-нибудь объяснить, почему в этом сценарии выбрана «Базовая перегрузка»? Мне интересно понять основные механизмы C++, которые приводят к такому поведению.
Если вы добавите метод override
, он будет работать так, как вы ожидаете Демо
Еще одна песочница, чтобы показать больше случаев Демо
«почему в этом сценарии выбрана «Базовая перегрузка»?...» Потому что Тип выражения &Derived::foo равен void (Base::*)(), а не void (Derived::*)()
Просто потому, что Derived
не объявляет свой собственный foo
, а наследует его от Base
. Если вы переопределите (или затените) его, тип изменится на ... (Derived::*)(...)
.
Иногда это полезно в шаблонах, чтобы определить, была ли определенная функция переопределена или нет.
Стоимость
std::is_same_v<decltype(&Derived::foo), decltype(&Base::foo)>
это true
, потому что ваш Dereived
не предоставляет собственного переопределения void foo
. Итак, void doSomething(void (Base::*foo)(void))
выбран. Оно изменится, если вы добавите void foo() override;
к своему Derived
.
&Derived::Foo
является/относится к&Base::Foo
как непереопределенному.