С++: обоснование правила скрытия
Какое обоснование правила скрытия на С++?
class A { void f(int); }
class B : public A { void f(double); } // B::f(int) is hidden
-
Если это значимая функция, я считаю, что также можно скрыть функции без определения новых функций с тем же именем: что-то вроде этого:
class B : public A { hide void f(double); }
но это невозможно.
-
Я не думаю, что это упрощает работу с компиляторами, поскольку компиляторы должны в любом случае иметь возможность отображать функции, когда вы явно используете директиву using
:
class B : public A { using A::f; void f(double); } // B::f(int) NOT hidden
Итак, как получилось правило скрытия?
Хум, все три ответа кажутся хорошими и показывают разные соображения для правила сокрытия. Я не уверен, какой ответ я должен принять.
Ответы
Ответ 1
Это волосатый вопрос, но, судя по всему, идея заключается в том, что эта функция скрытия помогает избежать тонких ошибок при внесении изменений в базовый класс (который в противном случае мог бы "украсть" вызовы, которые раньше были обработаны производным классом). Тем не менее изменение базового класса может повлиять на результат компиляции производных классов, поэтому я не думаю, что понимаю 100% это объяснение.
Я согласен, что эта тема так часто обсуждается, что, вероятно, скрытие на самом деле увеличивает количество "сюрпризов" в программистах на C++.
Подробное обсуждение этой проблемы можно найти здесь...
Ответ 2
Я не знаю оригинального логического обоснования, но поскольку скрыть или не скрыть, это примерно одинаково плохой выбор. к функциям, я предполагаю, что логическое обоснование состоит в том, чтобы иметь единые правила: то же, что и для имен, определенных в вложенных областях фигурных скобок.
скрытие помогает вам в некотором роде.
Добавление метода к базовому классу будет по умолчанию не влиять на разрешение перегрузки для производного класса.
и вы не выполняете решение о перегрузке с помощью какого-либо недоразумения, направляя ваш вызов с аргументом аргумента false
, на метод базового класса с формальным аргументом void*
. такие вещи.
приветствия и hth.,
Ответ 3
Я уверен, что видел этот случай, предложенный С++ bigwig, не уверен, что:
struct Base {
void f(const Base&);
};
struct Derived : Base {
using Base::f;
void f(double);
};
int main() {
Derived d;
d.f('a'); // calls Derived::f
}
Теперь добавьте void f(int);
в Base
и значение основных изменений - он вызывает Base::f
, потому что int
лучше подходит для char
- это целая реклама, а не стандартное преобразование.
Не ясно, будет ли это изменение для базы действительно программистом запрограммировать на вызов с помощью char
, поэтому требование using
быть явным означает, что поведение по умолчанию заключается в том, что это изменение не влияет на код вызова, Я считаю, что это маргинальный звонок, но я думаю, что комитет решил, что базовые классы на С++ были достаточно хрупкими, как они есть, без этого: -)
Нет необходимости в ключевом слове "hide", потому что нет сопоставимого случая для скрытия "f" от базы, когда он не перегружен в Derived.
Btw, я выбрал типы, а char
намеренно несовместим. Вы можете получить более тонкие случаи с int
vs unsigned int
, а не int
vs char
.
Ответ 4
Другая причина скрытия функции-члена базового класса (с тем же именем, но с разными сигнатурами) может быть вызвана неоднозначностью, вызванной необязательными параметрами. Рассмотрим следующий пример:
#include <stdio.h>
class A
{
public:
int foo(int a, int b=0)
{
printf("in A : %d, %d\n", a, b);
}
};
class B : public A
{
public:
int foo(int a)
{
printf("in B : %d\n", a);
foo(a); //B:foo(a) will be called unless we explicitly call A:foo(a)
foo(a, 1); // compile error: no matching function for call to B:foo(int&, int)
}
};
int main()
{
B b;
b.foo(10);
return 0;
}
Если метод foo
в базовом классе не стал скрытым, было бы невозможно, чтобы компилятор решил, следует ли вызывать A::foo
или B::foo
, поскольку следующая строка соответствует обеим подписям:
foo(a);
Ответ 5
Возможно, причина в шаблонной специализации. Я приведу вам пример:
template <int D> struct A { void f() };
template <> struct A<1> { void f(int) };
template <int D>
struct B: A<D>
{
void g() { this->f(); }
};
Класс B класса имеет метод f()
, но пока вы не создадите экземпляр класса B, вы не знаете подпись. Таким образом, вызов this->f()
всегда "юридический". Как GCC, так и CLang не сообщают об ошибке до создания экземпляра. Но когда вы вызываете метод g()
в экземпляре B<1>
, они указывают на ошибку. Таким образом, правило скрытия упрощает проверку правильности кода.
Я сообщаю последнюю часть кода, используемую в моем примере.
int main (int argc, char const *argv[])
{
B<0> b0; /* valid */
B<1> b1; /* valid */
b0.g(); /* valid */
b1.g(); /* error: no matching function for call to ‘B<1>::f()’ */
return 0;
}