Переопределение перегруженной базы в С++
Возможный дубликат:
Разрешение перегрузки С++
Я столкнулся с проблемой, когда после того, как мой класс переопределил функцию своего базового класса, все перегруженные версии функций были скрыты. Является ли это по дизайну или я просто делаю что-то неправильно?
Исх.
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);
}
Ответы
Ответ 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
Это по дизайну. Разрешение перегрузки ограничено одной областью. Это предотвращает некоторые неприятные случаи действительного изменения кода, когда дополнительные функции добавляются в базовый класс или в область пространства имен.