Подтвердить что ты не робот

Переопределение перегруженной базы в С++

Возможный дубликат:
Разрешение перегрузки С++

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

Исх.

class foo
{
  public:
    foo(void);
    ~foo(void);
    virtual void a(int);
    virtual void a(double);
};

class bar : public foo 
{
  public:
    bar(void);
    ~bar(void);
    void a(int);
};

следующее затем даст ошибку компиляции, говорящей, что в баре нет (двойной) функции.

main() 
{
  double i = 0.0;
  bar b;
  b.a(i);
}
4b9b3361

Ответ 1

В строке класса добавьте

using foo::a;

Это обычная "gotcha" в С++. Когда совпадение имен найдено в области класса, оно больше не смотрит дерево наследования для перегрузок. Указав декларацию 'using', вы переносите все перегрузки 'a' из 'foo' в область "bar". Тогда перегрузка работает правильно.

Имейте в виду, что если существует существующий код с использованием класса "foo", его значение может быть изменено дополнительными перегрузками. Или дополнительные перегрузки могут ввести двусмысленность, и код не будет скомпилирован. Об этом говорится в ответе Джеймса Хопкина.

Ответ 2

Так работает язык. До ключевого слова using, если вы перевернули одну перегруженную функцию, вам пришлось перегрузить все:

class bar : public foo 
{
  public:
    bar(void);
    ~bar(void);
    a(int);
    a(double d) { foo::a(d); }  // add this
}

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

Как отмечает Джеймс Хопкинс, добавив использование, программист выражает намерение, что производный класс без предупреждения добавит любые будущие переопределения foo:: a() в список допустимых подписей.

Вот пример того, что он описывает:

#include <iostream>
class Base {
public:
  virtual void f(double){ std::cout << "Base::Double!" << std::endl; }
  // virtual void f(int) { std::cout << "Base::Int!" << std::endl; } // (1)
  virtual ~Base() {}
};

class Derived : public Base {
public:
  // using Base::f; // (2)
  void f(double) { std::cout << "Derived::Double!" << std::endl; }
};

int main(int, char **) {
  Derived d;
  d.f(21);
  return 0;
}

Выход будет "Derived: Double!" потому что компилятор будет продвигать целочисленный аргумент в double. g++ 4.0.1 -Wall не будет предупреждать, что это продвижение произошло.

Uncomment (1), чтобы имитировать будущие изменения в Base, добавляя метод Base:: f (int). Код компилируется, снова без предупреждения даже с -Wall и "Derived:: Double!". остается выходом.

Теперь раскомментируйте (2), чтобы имитировать решение производного программиста включить все подписи Base:: f. Код компилируется (без предупреждений), но выход теперь "Base:: Int!".

& крестик; Я не могу придумать английского слова "те, у кого есть привычка", и "зависимый" слишком сильно.

Ответ 3

Это по дизайну. Разрешение перегрузки ограничено одной областью. Это предотвращает некоторые неприятные случаи действительного изменения кода, когда дополнительные функции добавляются в базовый класс или в область пространства имен.