C ++: обоснование скрытого правила - PullRequest
25 голосов
/ 29 января 2011

В чем смысл правила сокрытия в C ++?

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
    

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


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

Ответы [ 5 ]

10 голосов
/ 29 января 2011

Это сложный вопрос, но, очевидно, идея заключается в том, что эта скрытая функция помогает избежать незаметных ошибок при внесении изменений в базовый класс (которые в противном случае могли бы «украсть» вызовы, которые раньше обрабатывались бы производным классом).Тем не менее, изменение базового класса может повлиять на результат компиляции производных классов, поэтому я не думаю, что я понимаю это объяснение на 100%.

Я согласен, что эта тема обсуждается так часто, что, вероятно, скрытие действительно увеличиваетсяколичество «сюрпризов» в программистах C ++.

Подробное обсуждение этой проблемы можно найти здесь ...

9 голосов
/ 29 января 2011

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

сокрытие помогает вам в некоторых отношениях.

добавление метода в базовый класс по умолчанию не повлияет на разрешение перегрузки для производного класса.

и вы не столкнетесь с проблемой разрешения перегрузки из-за какого-то случайного сбоя, направляющего ваш вызов с помощью аргумента say false, к методу базового класса с формальным аргументом void*.такие вещи.

ура & hth.,

7 голосов
/ 29 января 2011

Я уверен, что видел этот случай, предложенный Сиги, но не уверен, что:

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 означает, что поведение по умолчанию состоит в том, что изменение не не влияет на код вызова. Я считаю, что это маргинальный вызов, но я думаю, что комитет решил, что базовые классы в C ++ были достаточно хрупкими, без них тоже: -)

Нет необходимости в ключевом слове «скрывать», потому что нет сопоставимого случая для сокрытия «f» от базы, когда оно не перегружено в производном.

Кстати, я выбрал типы, а char намеренно неуместен. Вы можете получить более тонкие случаи с int против unsigned int вместо int против char.

3 голосов
/ 16 января 2012

Другая причина скрытия функции-члена базового класса (с тем же именем, но разными сигнатурами) может быть связана с неоднозначностью, вызванной необязательными параметрами. Рассмотрим следующий пример:

#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);
0 голосов
/ 31 января 2011

Вероятно, причина в специализации шаблона.Я приведу вам пример:

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;
}
...