Означает ли функция переопределение базовой функции?
У меня есть три разных компилятора, которые я использую для компиляции этого кода. Один из них (тот, которому я доверяю меньше всего) предупреждает, что функция в Derived скрывает функцию в Base. Другие компиляторы (один Visual С++) не предупреждают. Visual С++ даже не дает предупреждения об этом, если я включаю /Wall или/W4.
Я склонен полагать, что это ошибка в компиляторе, которая дает предупреждение, поскольку он компилирует код. Если это действительно не переопределило базовую функцию, тогда она должна дать ошибку, когда я создаю экземпляр производного шаблона.
Может ли кто-нибудь подтвердить, как это должно себя вести?
struct Base
{
virtual void Func(float f) = 0;
};
template <typename T>
struct Derived : Base
{
virtual void Func(T f){}
};
int main()
{
Derived<float> d;
d.Func(0);
return 0;
}
Когда Derived
создается с помощью float
, я получаю неожиданное предупреждение. Когда Derived
создается с помощью int
, я получаю ошибку, как и ожидалось.
Ответы
Ответ 1
Это действительно переопределено. Вы легко можете убедить себя в С++ 11, используя ключевое слово override
, которое не позволит компилировать код, если функция не переопределена:
struct Base
{
virtual void Func(float f) = 0;
virtual ~Base() = default; // to silence warnings
};
template <typename T>
struct Derived : Base
{
void Func(T f) override {} // will fail to compile if not overriding
};
int main()
{
Derived<float> d;
d.Func(0);
return 0;
}
Живой пример здесь.
Обратите внимание, что в pre С++ 11 вы можете случайно скрыть базовую функцию virtual
, изменив ее подпись в производном классе, поэтому, даже если вы помечаете производную функцию virtual
, код все еще компилируется, но делает это не полиморфный, см. такой пример здесь. К сожалению, g++ не предоставляет никаких предупреждений даже при -Wall -Wextra
. Вот почему override
является гораздо более безопасным способом фактического применения true override во время компиляции.
Ответ 2
Я не верю, что вам следует дать предупреждение.
Это то же самое, что:
struct Derived : Base
{
virtual void Func(float f) { };
};
Если ваш параметр шаблона float
.
Нет скрытия, только реализация абстрактной функции.
Ответ 3
В этом контексте предупреждение о том, что функция скрыта, позволяет нам знать, что функция-член в производном классе имеет одно и то же имя, но другая подпись, чем функция в базовом классе. Рассмотрим:
struct Base
{
void foo(int) {}
void bar(int) {}
};
struct Derived: Base
{
void bar(int, int) {}
};
int main()
{
Derived d;
d.foo(1);
d.bar(1); // will not compile: Base::bar is hidden by Derived::bar
}
В этом примере, возможно, было намерение добавить дополнительную функцию с именем "bar", но результатом является то, что компилятор перестает искать новые области с панелью имен функций, как только он найдет область с функцией с именем bar. Таким образом, bar (int) скрывается баром (int, int) (или любым другим баром с неподходящей сигнатурой). (Или в не виртуальном случае, даже если функции соответствуют.)
В этом коде Гразнарака Base:: Func скрыт в любой ситуации, когда Derived создается для любого значения T, которое не является float (или float const).
Гразнарак спрашивает о правильном поведении. Правильное значение для сгенерированного кода не подлежит. Производится:: Вызывается Func().
Но это оставляет вопрос: правильно ли это предупреждение. Стандарт не имеет ответа. Он никогда не высказывает мнения о том, следует ли создавать предупреждение. О том, следует ли предупреждать о каких-либо конкретных проблемах, всегда субъективно, а компиляторы могут отличить себя, демонстрируя хорошее мнение в этом отношении.
Так должен ли ваш компилятор предупредить об этой ситуации? Возможно, написанный код делает то, что, вероятно, предназначено. Но наличие шаблона подразумевает, что оно будет создаваться более чем одним типом (иначе зачем создавать шаблон), а для любого другого типа скрывается. Поэтому можно утверждать, что предупреждение должно быть дано с созданием производного шаблона. Но можно также утверждать, что предупреждение не должно происходить до тех пор, пока не будет указано экземпляр типа non-float.
Рассуждение для первого заключается в том, что предупреждение будет раньше и, вероятно, обнаружено программистом, написавшим проблематичный код. Рассуждение для более позднего состоит в том, что до тех пор, пока не будет создан экземпляр неплавающего типа, о подозрительной ситуации не должно быть предупреждено.