27

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
    

では、なぜ隠蔽ルールがあるのでしょうか。


ふむ、3つの答えはすべて良いようで、隠蔽ルールのさまざまな根拠を示しています。どの答えを受け入れるべきかわかりません。

4

5 に答える 5

12

これは厄介な質問ですが、この非表示機能は、基本クラスに変更を加えるときに微妙なバグを回避するのに役立つようです(そうでなければ、以前は派生クラスによって処理されていた呼び出しを「盗む」可能性があります)。それでも、基本クラスの変更は派生クラスのコンパイル結果に影響を与える可能性があるため、この説明を100%理解しているとは思いません。

私は、このトピックが非常に頻繁に議論されているため、おそらく非表示にすると、C++プログラマーの「驚き」の量が実際に増えることに同意します。

この問題に関する詳細な議論はここで見つけることができます...

于 2011-01-29T14:55:18.977 に答える
10

元々の論理的根拠はわかりませんが、非表示にするか非表示にするかは、同じように悪い選択です。関数の場合、論理的根拠は統一されたルールを持つことだと思います。ネストされた中括弧スコープで定義された名前の場合と同じです。

隠すことはいくつかの点であなたを助けます。

基本クラスにメソッドを追加しても、デフォルトでは派生クラスの過負荷解決には影響しません。

falseまた、say引数を使用した呼び出しを、仮引数を使用した基本クラスのメソッドに誤って指示することによって、過負荷の解決に反することはありませんvoid*。そのようなもの。

乾杯&hth。、

于 2011-01-29T15:03:34.817 に答える
8

このケースがC++ビッグウィッグによって提供されているのを見たことがあると思いますが、どちらかはわかりません。

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 ++の基本クラスは、これがなくても、そのままでも十分に壊れやすいと判断したと思います:-)

Derivedでオーバーロードされていないときに、ベースから「f」を非表示にする同等のケースがないため、「hide」キーワードは必要ありません。

ところで、私はタイプを選択しました、そしてchar故意に違和感があります。intvsunsigned intではなくintvsを使用すると、より微妙なケースを取得できますchar

于 2011-01-29T16:44:20.107 に答える
3

基本クラスのメンバー関数(同じ名前で署名が異なる)を非表示にするもう1つの理由は、オプションのパラメーターによって引き起こされるあいまいさが原因である可能性があります。次の例を考えてみましょう。

#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);
于 2012-01-16T12:28:33.157 に答える
-2

おそらく、その理由はテンプレートの特殊化です。私はあなたに例をあげます:

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;
}
于 2011-01-31T16:44:28.877 に答える