#опытным
2 вещи нужно знать(помимо всего остального С++😆), чтобы правильно ответить на квиз выше:
1️⃣ Сокрытие имен
Когда в классе объявляют метод с неким именем, все методы с этим именем из базовых классов становятся невидимыми (скрытыми), независимо от их параметров.
То есть
struct A {
void func(const std::string&);
};
struct B : A {
void func(float);
};
B b; у объекта
b можно вызвать только флотовый вариант func.Почему так?
Компилятор увидел вызов метода и должен выполнить overload resolution. Он видит, что у объекта
b статический тип B и идет смотреть, какие методы из этого класса подходят для вызова.И вот здесь срабатывает ключевая особенность поиска. Компилятор ищет имя
func по цепочке областей видимости — снизу вверх (от производного класса к базовому). Как только он находит хотя бы одно объявление с именем func, поиск по иерархии немедленно останавливается . Дальше вверх (в A) он уже не заглядывает. В классе
B есть func(float) — имя найдено. Поиск завершён. В набор кандидатов для overload resolution попадает только B::func(float). Метод A::func(const std::string&) в этот набор даже не рассматривается — он остался за границей поиска. Поэтому:
b.func(15.f); // OK: float
b.func("hello"); // Compile Error
2️⃣ Вообще говоря, это сокрытие имен - это проблема с точки зрения привычного нам ООП.
"У меня есть базовый класс, от которого я наследую функциональность. Почему я не могу использовать метод базового класса?"
Сокрытие имен - процесс неявный. Но мы можем явно сказать, какой сокрытый метод мы как разработчики хотим видеть в наследнике.
С помощью директивы
using можно вернуть видимость методу базового класса в наследник:struct A {
void func(const std::string&);
};
struct B : A {
using A::func;
void func(float);
};
B b;
b.func(15.f); // OK
b.func("hello"); // OKusing A::func; позволяет компилятору увидеть метод, принимающий строку, и успешно вызывать его при передаче строкового литерала.Такой же прием кстати используется в реализации overloaded паттерна.
Когда мы это знаем, правильные ответы находятся на поверхности:
struct A {
void func(const std::string&); // #1
};
struct B : A {
void func(float); // #2
};
struct C : B {
using A::func;
void func(int); // #3
};
C c;
B& b = c;
c.func(3.14f); // вызывает #3 за счет неявного приведения float к int
c.func(123); // вызывает #3
c.func("hello"); // вызывает #1 за счет using
b.func(3.14f); // вызывает #2Don't let others shadow you. Stay cool
#cppcore