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

С++: обоснование правила скрытия

Какое обоснование правила скрытия на С++?

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
    

Итак, как получилось правило скрытия?


Хум, все три ответа кажутся хорошими и показывают разные соображения для правила сокрытия. Я не уверен, какой ответ я должен принять.

4b9b3361

Ответ 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;
}