57

プロジェクトでサブクラスのtypedefを使用しようとしていますが、以下の例で問題を特定しました。

誰かが私がどこで間違っているのか知っていますか?

template<typename Subclass>
class A {
    public:
        //Why doesn't it like this?
        void action(typename Subclass::mytype var) {
            (static_cast<Subclass*>(this))->do_action(var);
        }
};

class B : public A<B> {
    public:
        typedef int mytype;

        B() {}

        void do_action(mytype var) {
            // Do stuff
        }
};

int main(int argc, char** argv) {
    B myInstance;
    return 0;
}

これは私が得る出力です:

sean@SEAN-PC:~/Documents/LucadeStudios/experiments$ g++ -o test test.cpp
test.cpp: In instantiation of ‘A<B>’:
test.cpp:10:   instantiated from here
test.cpp:5: error: invalid use of incomplete type ‘class B’
test.cpp:10: error: forward declaration of ‘class B’
4

5 に答える 5

68

その理由は、クラス テンプレートをインスタンス化するときに、そのメンバー関数のすべての宣言 (定義ではない) もインスタンス化されるためです。クラス テンプレートは、特殊化の完全な定義が必要なときに正確にインスタンス化されます。これは、たとえば、あなたの場合のように基本クラスとして使用される場合です。

何が起こるかというA<B>と、

class B : public A<B>

その時点Bでは、まだ完全な型ではありません (クラス定義の右中括弧の後です)。ただし、のスコープ内でクロールしているため、A<B>::actionの宣言は完全である必要があります。B

Subclass::mytype

あなたがする必要があるのは、インスタンス化Bが完了するまで遅らせることです。これを行う 1 つの方法は、の宣言を変更しactionてメンバー テンプレートにすることです。

template<typename T>
void action(T var) {
    (static_cast<Subclass*>(this))->do_action(var);
}

が正しい型でない場合、へvarの引き渡しは失敗するため、これはまだ型安全です。vardo_action

于 2009-03-16T21:36:47.770 に答える
28

特性クラスを使用してこれを回避
できます。使用する実際のクラスごとに特別な特性クラスを設定する必要があります。

template<typename SubClass>
class SubClass_traits
{};

template<typename Subclass>
class A {
    public:
        void action(typename SubClass_traits<Subclass>::mytype var)
        {
                (static_cast<Subclass*>(this))->do_action(var);
        }
};


// Definitions for B
class B;   // Forward declare

template<> // Define traits for B. So other classes can use it.
class SubClass_traits<B>
{
    public:
        typedef int mytype;
};

// Define B
class B : public A<B>
{
    // Define mytype in terms of the traits type.
    typedef SubClass_traits<B>::mytype  mytype;
    public:

        B() {}

        void do_action(mytype var) {
                // Do stuff
        }
};

int main(int argc, char** argv)
{
    B myInstance;
    return 0;
} 
于 2009-03-16T22:07:47.540 に答える
2

Bから派生するA<B>ため、クラスの定義を確認すると、コンパイラが最初に行うことは、Bインスタンス化を試みることA<B>です。これを行うにはB::mytype、 のパラメータを知る必要がありますaction。しかし、コンパイラは の実際の定義を把握している最中であるため、Bこの型はまだ認識されておらず、エラーが発生します。

これを回避する 1 つの方法は、派生クラス内ではなく、別のテンプレート パラメーターとしてパラメーターの型を宣言することです。

template<typename Subclass, typename Param>
class A {
    public:
        void action(Param var) {
                (static_cast<Subclass*>(this))->do_action(var);
        }
};

class B : public A<B, int> { ... };
于 2009-03-16T21:29:29.053 に答える
1

まさにあなたが求めていたものではありませんが、アクションをテンプレートメンバー関数にすることができます:

template<typename Subclass>
class A {
    public:
        //Why doesn't it like this?
        template<class V> void action(V var) {
                (static_cast<Subclass*>(this))->do_action();
        }
};

class B : public A<B> {
    public:
        typedef int mytype;

        B() {}

        void do_action(mytype var) {
                // Do stuff
        }
};

int main(int argc, char** argv) {
    B myInstance;
    return 0;
}
于 2009-03-16T21:45:05.240 に答える
0

現時点では適切な型がわからないため、コンパイラはインスタンス化できないため、ポインターまたは参照を使用する必要があります。

代わりに試してください:

void action(const typename Subclass::mytype &var) {
            (static_cast<Subclass*>(this))->do_action();
    }
于 2009-03-16T21:17:15.370 に答える