Недопустимое использование неполного типа
Я пытаюсь использовать 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;
}
Это результат, который я получаю:
[email protected]:~/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’
Ответы
Ответ 1
Причина в том, что при создании экземпляра шаблона класса все его объявления (а не определения) его функций-членов также создаются. Шаблон класса создается именно тогда, когда требуется полное определение специализации. Это тот случай, когда он используется как базовый класс, например, как в вашем случае.
Итак, что происходит, когда A<B>
создается в
class B : public A<B>
в котором точка B
еще не является полным типом (она находится после закрывающей скобки определения класса). Однако для объявления A<B>::action
требуется, чтобы B
был полным, потому что он сканирует его область действия:
Subclass::mytype
Что вам нужно сделать, так это отложить создание экземпляра до некоторой точки, в которой завершено B
. Один из способов сделать это - изменить объявление action
, чтобы сделать его шаблоном-членом.
template<typename T>
void action(T var) {
(static_cast<Subclass*>(this))->do_action(var);
}
Он по-прежнему безопасен по типу, потому что если var
не имеет нужного типа, передача var
в do_action
завершится с ошибкой.
Ответ 2
Вы можете обойти это, используя класс признаков:
Это требует, чтобы вы создали специальный класс признаков для каждого используемого класса.
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;
}
Ответ 3
Вы получаете B
от A<B>
, поэтому первое, что делает компилятор, когда он видит определение класса B
, - это попытаться создать экземпляр A<B>
. Для этого необходимо знать B::mytype
для параметра action
. Но поскольку компилятор сейчас находится в процессе определения фактического определения B
, он еще не знает этого типа, и вы получите сообщение об ошибке.
Один из способов заключается в том, чтобы объявить тип параметра в качестве другого параметра шаблона, а не внутри производного класса:
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> { ... };
Ответ 4
Не совсем то, что вы просили, но вы можете сделать действие функцией члена шаблона:
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;
}
Ответ 5
Вам нужно использовать указатель или ссылку, поскольку соответствующий тип не известен в это время, когда компилятор не может его создать.
Вместо этого попробуйте:
void action(const typename Subclass::mytype &var) {
(static_cast<Subclass*>(this))->do_action();
}