16
#include <iostream>

void foo()
{
    std::cout << "global foo()" << std::endl;
}

struct A {
    void foo()
    {
        std::cout << "A::foo()" << std::endl;
    }
};

struct B : public A {
    void call()
    {
        foo();
    }
};

int main(int argc, char **argv )
{
    B b;
    b.call();
    return 0;
}

これにより、期待される結果が得られます。

A::foo()

ただし、2 行を変更した後 (クラス B からテンプレート):

#include <iostream>

void foo()
{
    std::cout << "global foo()" << std::endl;
}

struct A {
    void foo()
    {
        std::cout << "A::foo()" << std::endl;
    }
};

template <typename T> // change here
struct B : public T {
    void call()
    {
        foo();
    }
};

int main(int argc, char **argv )
{
    B<A> b; // and here
    b.call();
    return 0;
}

予期しない結果が得られます:

global foo()

またthis->、「フォールバック」メカニズムを作成しようとしているため、使用はオプションではありません。

4

3 に答える 3

16

あなたが得るものは期待される結果です。これは、C++標準では「2フェーズ名ルックアップ」と呼ばれます。

テンプレート内の名前は、次の2つのタイプに分けられます。

依存–テンプレートパラメータに依存するが、テンプレート内で宣言されていない名前。

非依存–テンプレートパラメータに依存しない名前に加えて、テンプレート自体の名前とその中で宣言されている名前。

コンパイラがコード内の名前を解決しようとすると、最初にその名前が依存しているかどうかが判断され、解決プロセスはこの区別に基づいて行われます。非依存名は「通常」に解決されますが、テンプレートが定義されると、依存名の解決はテンプレートのインスタンス化の時点で行われます。

foo();この例でB::callは、依存しない名前であるためfoo()、テンプレート定義の時点でグローバルに解決されます。

于 2012-04-20T14:54:47.133 に答える
3

受け入れられた回答は、その動作が見られる理由を説明していますが、必要な「フォールバック」動作を達成する方法は説明していません。これは、 SFINAEを使用して、メンバー テンプレート オーバーロードのペアを導入することで実行できます。そのうちの 1 つは、基本クラスに というメンバー関数がある場合にのみ存在しますfoo

template <typename T>
struct B : T {
    template <void (T::*)()> struct has_mem_fn {};

    template <typename U> void call(has_mem_fn<&U::foo>*) {this->foo();}
    template <typename U> void call(...) {foo();}

    void call() {call<T>(0);}
};

struct X {};

int main()
{
    B<A> ba;
    ba.call();  // A::foo()

    B<X> bx;
    bx.call();  // global foo()
}

更新:別の回答であなたのコメントに気づきました.この方法を知っていると言っていますが、機能不全のコンパイラをサポートする必要があるため使用できません. その場合、あなたが望むことはおそらく不可能だと思います。

于 2012-04-20T16:07:01.573 に答える
0

Tクラスメソッドを使用するように具体的に指示する必要があります。

template <typename T>
struct B : public T {
    void call()
    {
        T::foo();
    }
};


しかし、フォールバックメカニズムに関しては、次の質問を確認できます。関数の存在を確認するためのテンプレートを作成することは可能ですか?

置換失敗の使用はエラーではありません(SFINAE)。Tでメソッドを確認してからfoo、適切なメソッドを実行できます。

于 2012-04-20T14:54:09.890 に答える