web-dev-qa-db-ja.com

派生クラスで同じ名前で異なるシグネチャを持つ関数

同じ名前の関数がありますが、基本クラスと派生クラスのシグネチャが異なります。派生クラスから継承する別のクラスで基本クラスの関数を使用しようとすると、エラーが発生します。次のコードを参照してください。

_class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};
_

Gccコンパイラから次のエラーを受け取ります。

_In member function `void C::bar()': no matching function for call to `C::foo(std::string&)' candidates are: int B::foo(int)
_

クラスBからint foo(int i){};を削除するか、_foo1_から名前を変更すると、すべて正常に動作します。

これの問題は何ですか?

83
Igor Oks

基本クラスの関数をオーバーライドしないが、同じ名前を持つ派生クラスの関数は、hide基本クラスの同じ名前の他の関数になります。

基底クラスの関数をオーバーライドすることを目的としていない基底クラスの関数と同じ名前を持つ派生クラスの関数を使用することは、一般的に悪い習慣と考えられています。通常、異なる関数に異なる名前を付けることが望ましいです。

ベース関数を呼び出す必要がある場合は、A::foo(s)を使用して呼び出しをスコープする必要があります。これにより、A::foo(string)の仮想関数メカニズムも同時に無効になります。

72
CB Bailey

いずれかのベースで名前が見つかった場合、名前の検索が停止するためです。他の拠点を超えては見えません。 Bの関数shadows Aの関数。BとC内から両方の関数が見えるように、BのスコープでAの関数を再宣言する必要があります。

class A
{
    public:
    void foo(string s){};
};

class B : public A
{
    public:
    int foo(int i){};
    using A::foo;
};

class C : public B
{
    public:
    void bar()
    {
        string s;
        foo(s);
    }
};

編集:標準が与える本当の説明は(10.2/2から)です:

次の手順は、クラススコープCでの名前ルックアップの結果を定義します。最初に、クラス内およびその各基本クラスサブオブジェクト内の名前のすべての宣言が考慮されます。 1つのサブオブジェクトBのメンバー名fは、AがBの基本クラスのサブオブジェクトである場合、サブオブジェクトAのメンバー名fを隠します。そのように隠されている宣言は考慮から除外されます。 using-declarationによって導入されたこれらの各宣言は、using-declarationによって指定された宣言を含む型のCの各サブオブジェクトからのものと見なされます。96)結果の宣言セットがすべてが同じタイプのサブオブジェクトから、またはセットに非静的メンバーがあり、異なるサブオブジェクトからのメンバーが含まれている場合、あいまいさがあり、プログラムの形式が正しくありません。それ以外の場合、そのセットはルックアップの結果です。

別の場所(そのすぐ上)で次のように言います:

Id-expression ["foo"]の場合、名前の検索はこのクラススコープで開始されます。修飾ID ["A :: foo"のようなもの、Aはネストされた名前指定子]の場合、名前の検索はネストされた名前指定子のスコープで開始されます。名前の検索は、アクセス制御の前に行われます(3.4、条項11)。

([...]私が置いた)。これは、Bのfooがプライベートであっても、Aのfooがまだ見つからないことを意味することに注意してください(アクセス制御は後で発生するため)。

103