次のスニペットは、コンパイル中に「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;
}
メンバールックアップルールはセクション10.2/2で定義されています
次の手順では、クラススコープ
C
での名前ルックアップの結果を定義します。最初に、クラスおよびその各基本クラスサブオブジェクト内の名前のすべての宣言が考慮されます。 1つのサブオブジェクト内のメンバー名f
B
は、サブオブジェクト内のメンバー名f
を非表示にしますA
A
がB
の基本クラスサブオブジェクト。 そのように隠されている宣言は考慮から除外されます。 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)
を呼び出します。
名前検索は過負荷解決とは別のフェーズです。
名前の検索が最初に行われます。これは、名前を適用するスコープを決定するプロセスです。この場合、_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つの別個の関数であるかどうかに違いはありません。
それはあなたのために働きますか?
struct Derived : public Base1, public Base2{
using Base2::foo;}