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

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

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

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

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;
}

Ответ 1

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

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

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

Ответ 2

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

В основном компилятор находит совпадение имени "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;
}

Ответ 3

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

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

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

Ответ 4

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

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), может бесшумно повлиять на работу вашего кода.

Ответ 5

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

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

... [надрез]...

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

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

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

Ответ 9

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