web-dev-qa-db-ja.com

同じ名前で異なるシグネチャを持つ複数の継承関数がオーバーロードされた関数として扱われないのはなぜですか?

次のスニペットは、コンパイル中に「fooへのあいまいな呼び出し」エラーを生成します。fooへの呼び出しを完全に修飾せずにこの問題を回避する方法があるかどうかを知りたいです。

#include <iostream>

struct Base1{
    void foo(int){
    }
};

struct Base2{
    void foo(float){
    }
};

struct Derived : public Base1, public Base2{
};

int main(){
    Derived d;
    d.foo(5);

    std::cin.get();
    return 0;
}

だから、質問はタイトルが言うようです。アイデア?つまり、次の機能は問題なく機能します。

#include <iostream>

struct Base{
    void foo(int){
    }
};

struct Derived : public Base{
    void foo(float){
    }
};

int main(){
    Derived d;
    d.foo(5);

    std::cin.get();
    return 0;
}
49
Xeo

メンバールックアップルールはセクション10.2/2で定義されています

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

_class A {
public:
  int f(int);

};
class B {
public:
   int f();

};
class C : public A, public B {};
int main()
{
     C c;
     c.f(); // ambiguous
}
_

したがって、using宣言_A::f_および_B::f_を使用して、そのあいまいさを解決できます。

_class C : public A, public B {
     using A::f;
     using B::f;

};

int main()
{
     C c;
     c.f(); // fine
}
_

void foo(float)はCのスコープ内にあるため、2番目のコードは問題なく機能します。実際には、d.foo(5);intバージョンではなくvoid foo(float)を呼び出します。

48
Prasoon Saurav

名前検索過負荷解決とは別のフェーズです。

名前の検索が最初に行われます。これは、名前を適用するスコープを決定するプロセスです。この場合、_d.foo_が_d.D::foo_を意味するか、_d.B1::foo_を意味するか、_d.B2::foo_を意味するかを決定する必要があります。名前検索ルールは、関数パラメーターなどを考慮しません。それは純粋に名前とスコープに関するものです。

その決定がなされた後でのみ、名前が見つかったスコープ内の関数のさまざまなオーバーロードに対してオーバーロード解決を実行しますか。

あなたの例では、d.foo()を呼び出すと、そのような関数があればD::foo()が見つかります。しかし、何もありません。したがって、スコープを逆方向に処理して、基本クラスを試行します。これで、fooは_B1::foo_または_B2::foo_を等しく検索できるため、あいまいになります。

同じ理由で、Dメンバー関数内で修飾されていないfoo(5);を呼び出すとあいまいになります。


推奨されるソリューションの効果:

_struct Derived : public Base1, public Base2{
    using Base1::foo;
    using Base2::foo;
_

これにより、名前_D::foo_が作成され、2つの関数が識別されます。その結果、_d.foo_は_d.D::foo_に解決され、_D::foo_で識別されるこれら2つの関数で過負荷解決が発生する可能性があります。

注:この例では、D::foo(int)Base1::foo(int)は1つの関数の2つの識別子です。ただし、一般に、名前の検索と過負荷の解決プロセスでは、2つの別個の関数であるかどうかに違いはありません。

6
M.M

それはあなたのために働きますか?

struct Derived : public Base1, public Base2{
   using Base2::foo;}
2
a1ex07