質問
プロジェクトのサブクラスから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’
解決
その理由は、クラステンプレートをインスタンス化すると、そのメンバー関数のすべての宣言(定義ではなく)もインスタンス化されるためです。クラステンプレートは、スペシャライゼーションの完全な定義が必要な場合に正確にインスタンス化されます。それは、あなたの場合のように、例えば、基底クラスとして使用される場合です。
つまり、A<B>
は
class B : public A<B>
でポイントB
はまだ完全な型ではありません(クラス定義の右中括弧の後)。ただし、A<B>::action
の宣言は、スコープ内でクロールしているため、action
が完了する必要があります。
Subclass::mytype
行う必要があるのは、インスタンス化をvar
が完了する時点まで遅らせることです。これを行う1つの方法は、do_action
の宣言を変更して、それをメンバーテンプレートにすることです。
template<typename T>
void action(T var) {
(static_cast<Subclass*>(this))->do_action(var);
}
<=>が正しいタイプでない場合、<=>を<=>に渡すと失敗するため、タイプセーフです。
他のヒント
traitsクラスを使用してこれを回避できます:
使用する実際のクラスごとに特別な特性クラスを設定する必要があります。
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;
}
B
をA<B>
から派生させるため、コンパイラが最初に行うことは、クラスB::mytype
の定義を確認すると、action
のインスタンス化を試みることです。これを行うには、<=>のパラメータの既知の<=>が必要です。しかし、コンパイラは<=>の実際の定義を理解する過程にあるため、この型はまだわからず、エラーが発生します。
これを回避する方法の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> { ... };
まさにあなたが求めていたものではありませんが、アクションをテンプレートメンバー関数にすることができます:
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;
}
適切な型は現時点ではコンパイラーがインスタンス化できないため、ポインターまたは参照を使用する必要があります。
代わりに試す:
void action(const typename Subclass::mytype &var) {
(static_cast<Subclass*>(this))->do_action();
}