Разрешение перегрузки C++

В следующем примере, почему я должен явно использовать оператор b->A::DoSomething(), а не просто b->DoSomething()?

Разве разрешение перегрузки компилятора не должно определять, о каком методе я говорю?

Я использую Microsoft VS 2005. (Примечание: использование virtual в этом случае не помогает.)

class A
{
  public:
    int DoSomething() {return 0;};
};

class B : public A
{
  public:
    int DoSomething(int x) {return 1;};
};

int main()
{
  B* b = new B();
  b->A::DoSomething();    //Why this?
  //b->DoSomething();    //Why not this? (Gives compiler error.)
  delete b;
  return 0;
}

Я пытаюсь вызвать DoSomething () в классе A, используя указатель на класс B.

Abe 16.09.2008 17:22
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
39
1
11 764
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Ответ принят как подходящий

Две «перегрузки» не относятся к одной области. По умолчанию компилятор рассматривает только наименьшую возможную область имени, пока не найдет совпадение имени. Сопоставление аргументов выполняется после. В вашем случае это означает, что компилятор видит B::DoSomething. Затем он пытается сопоставить список аргументов, что терпит неудачу.

Одним из решений было бы перенести перегрузку из A в область B:

class B : public A {
public:
    using A::DoSomething;
    // …
}

Фактически, если они не находятся в одной области, они также не называются перегрузками.

Chubsdad 04.11.2010 07:18

@Chubsdad: Вот почему я заключил это слово в кавычки. И как только метод помещается в ту же область видимости, он становится перегрузкой.

Konrad Rudolph 04.11.2010 12:48

Разрешение перегрузки - одна из самых уродливых частей C++.

Обычно компилятор находит совпадение имени «DoSomething (int)» в области B, видит, что параметры не совпадают, и останавливается с ошибкой.

Это можно преодолеть, используя A :: DoSomething в классе B

class A  
{  
  public:  
    int DoSomething() {return 0;}
};  

class B : public A  
{  
  public:  
    using A::DoSomething;
    int DoSomething(int x) {return 1;} 
};   


int main(int argc, char** argv)
{
  B* b = new B();  
  // b->A::DoSomething();    // still works, but...
  b->DoSomething();    // works now too
  delete b;  
  return 0;
}

Когда вы определяете функцию в производном классе, она скрывает все функции с этим именем в базовом классе. Если функция базового класса виртуальная и имеет совместимую сигнатуру, тогда функция производного класса также переопределяет функцию базового класса. Однако это не влияет на видимость.

Вы можете сделать функцию базового класса видимой с помощью объявления using:

class B : public A  
{  
  public:  
    int DoSomething(int x) {return 1;};  
    using A::DoSomething;
};  

При поиске в дереве наследования функции, которую следует использовать, C++ использует имя без аргументов, как только он обнаруживает какое-либо определение, он останавливается, а затем проверяет аргументы. В приведенном примере он останавливается в классе B. Чтобы иметь возможность делать то, что вам нужно, класс B должен быть определен следующим образом:

class B : public A  
{  
  public:
    using A::DoSomething;
    int DoSomething(int x) {return 1;};  
}; 

Функция скрыта функцией с тем же именем в подклассе (но с другой подписью). Вы можете отобразить его, используя оператор using, как в случае использования A :: DoSomething ();

Нет, это поведение присутствует, чтобы гарантировать, что вы не попадете в ловушку наследования от удаленных базовых классов по ошибке.

Чтобы обойти это, вам нужно сообщить компилятору, какой метод вы хотите вызвать, поместив using A :: DoSomething в класс B.

См. Эта статья для быстрого и легкого обзора этого поведения.

Это как-то связано с тем, как работает разрешение имен. По сути, мы сначала находим область, из которой происходит имя, а затем собираем все перегрузки для этого имени в этой области. Однако в вашем случае область видимости - это класс B, а в классе B - B :: DoSomething прячется A :: DOSomething:

3.3.7 Скрытие имени [basic.scope.hiding]

... [отрывок] ...

3 В определении функции-члена объявление локального имени скрывает объявление члена класса с таким же именем; видеть basic.scope.class. Объявление члена в производном классе (класс. производный) скрывает объявление члена базового класса то же имя; см. class.member.lookup.

Из-за сокрытия имени A :: DoSomething даже не рассматривается для разрешения перегрузки.

Наличие актуальной ссылки на спецификацию - это хорошо.

Sam 05.01.2012 01:32

Это не перегрузка! Это СКРЫВАЕТСЯ!

Наличие метода в производном классе скрывает все методы с тем же именем (независимо от параметров) в базовых классах. Это сделано, чтобы избежать таких проблем:

class A {} ;
class B :public A
{
    void DoSomething(long) {...}
}

B b;
b.DoSomething(1);     // calls B::DoSomething((long)1));

чем позже кто-то меняет класс A:

class A
{
    void DoSomething(int ) {...}
}

теперь вдруг:

B b;
b.DoSomething(1);     // calls A::DoSomething(1);

Другими словами, если это не сработало, несвязанные изменения в классе, который вы не контролируете (A), могли бы незаметно повлиять на работу вашего кода.

Должен сказать, очень хороший момент. В большинстве ответов, которые я видел, говорилось о том, почему возникает ошибка и как ее избежать, но ваша точка зрения показывает, почему эта функция важна и полезна в сценарии реального мира !!

Pravar Jawalekar 20.11.2017 09:59

Другие вопросы по теме