27
struct A {
    void f(int x) {}
};

struct B {
    template<typename T> void f(T x) {}
};

struct C : public A, public B {};

struct D {
    void f(int x){}
    template<typename T> void f(T x) {} 
};


int main(int argc, char **argv) {
    C c;
    c.f<int>(3);
    D d;
    d.f<int>(3);
}

d.f呼び出しは問題ないが、c.f与える理由は何ですか

error: request for member ‘f’ is ambiguous
error: candidates are: template<class T> void B::f(T)
error:                 void A::f(int)
4

5 に答える 5

11

最初の部分はメンバー名のルックアップが原因であるため、失敗します。

私はあなたを紹介します:10.2/2 Member name lookup

次の手順では、クラススコープCでの名前ルックアップの結果を定義します。最初に、クラスおよびその各基本クラスサブオブジェクトでの名前のすべての宣言が考慮されます。1つのサブオブジェクトBのメンバー名fは、AがBの基本クラスサブオブジェクトである場合、サブオブジェクトAのメンバー名fを非表示にします。そのように非表示になっている宣言はすべて考慮から除外されます。using-declarationによって導入されたこれらの各宣言は、using-declarationによって指定された宣言を含むタイプのCの各サブオブジェクトからのものであると見なされます。

結果の宣言のセットがすべて同じタイプのサブオブジェクトからのものではない場合、またはセットに非静的メンバーがあり、別個のサブオブジェクトからのメンバーが含まれている場合、あいまいさがあり、プログラムの形式が正しくありません。それ以外の場合、そのセットはルックアップの結果です。

さて、テンプレート関数の問題について。

によると13.3.1/7 Candidate functions and argument list

候補が関数テンプレートである場合、候補関数テンプレートの特殊化は、テンプレート引数の演繹(14.8.3、14.8.2)を使用して生成されます。これらの候補は、通常の方法で候補関数として処理されます。指定された名前は、1つ以上の関数テンプレート、およびオーバーロードされた非テンプレート関数のセットを参照できます。このような場合、各関数テンプレートから生成された候補関数は、非テンプレート候補関数のセットと組み合わされます。

そして、あなたが読み続けるなら13.3.3/1 Best viable function

次の場合、F1はより優れた関数であると見なされます。

F1は非テンプレート関数であり、F2は関数テンプレートの特殊化です。

そのため、次のスニペットは非テンプレート関数をエラーなしでコンパイルして実行します。

D c;
c.f(1);
于 2012-04-02T11:19:40.590 に答える
1

コンパイラは理由もなくA::f(非テンプレート関数) を好むと思います。これは、実装に依存する詳細というよりも 、コンパイラの実装のバグのようです。B::f

次の行を追加すると、コンパイルが正常に行われ、正しい関数B::f<>が選択されます。

struct C : public A, public B { 
  using A::f; // optional
  using B::f;
};

[面白いのは、::fが のスコープに取り込まれないC限り、エイリアン関数として扱われることです。]

于 2012-04-02T11:36:57.133 に答える
0

int 型の場合、テンプレート化されたメソッドは void f(int) で変換されるため、コンパイラは C クラスから呼び出すメソッドを認識しません。そのため、同じ名前と同じ引数を持つ 2 つのメソッドがありますが、親クラスは異なります。

template<typename T> void f(T x) {} 

また

void f(int)

これを試して:

c.B::f<int>(3);

またはこれはAクラスの場合:

c.A::f(3);
于 2012-04-02T11:25:10.103 に答える
0

次の簡単な例を考えてみましょう。

struct A{
 void f(int x){}
};

struct B{
 void f(float t){}
};


struct C:public A,public B{
};

struct D{
 void f(float n){}
 void f(int n){}
};


int main(){
 C c;
 c.f(3);

 D d;
 d.f(3);
}

この例では、あなたのものと同じように、Dコンパイルしますが、Cしません。
クラスが派生クラスの場合、メンバー検索メカニズムは異なる動作をします。各基本クラスをチェックしてマージしますC。各基底クラスはルックアップ ( A::f(int) および B::f(float) ) に一致します。それらをマージCすると、あいまいであると判断されます。

ケース クラスの場合D:パラメータが整数であるため、int代わりにバージョンが選択されます。float

于 2012-04-02T11:38:21.063 に答える
0

おそらく起こっていることは、テンプレートのインスタンス化が classAとに対して別々に行われているため、2 つの関数Bで終わっていることです。void f(int)

これはD、コンパイラがvoid f(int)関数を特殊化として認識しているため、 では発生しませTint

于 2012-04-02T11:40:44.963 に答える