TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.36K subscribers
Post #1124 3.89K
​​Ответ
#опытным

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"); // OK


using 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); // вызывает #2


Don't let others shadow you. Stay cool

#cppcore
  • ❤ 24
  • 👍 23
  • 🔥 9
  • ❤‍🔥 1
  • 👎 1
  • 😱 1
More from @grokaemcpp
  1. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  2. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  3. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  4. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  5. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
  6. Sep 25, 2026​​std::spanstream #опытным Радостная весть для всех, кто пользуется iostreams! В C++23 доб…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →