استخدام غير صالح من نوع غير مكتملة
سؤال
وأنا أحاول أن استخدام الرموز المميزة ل 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’
المحلول
والسبب هو أنه عندما instantiating قالب فئة، ويتم إنشاء مثيل جميع إعلاناتها (وليس التعريفات) وظائف الأعضاء فيها أيضا. يتم إنشاء مثيل القالب الدرجة بالضبط عندما يطلب من التعريف الكامل للتخصص. هذا هو الحال عندما يتم استخدامها كطبقة قاعدة على سبيل المثال، كما هو الحال في قضيتك.
فما يحدث هو أن 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
.
نصائح أخرى
ويمكنك الحصول على حول هذا باستخدام فئة الصفات:
فإنه يتطلب منك إنشاء فئة الصفات متخصص من كل actuall الدرجة التي تستخدمها.
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
الطبقة هو محاولة مثيل 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> { ... };
وليس بالضبط ما كنت طالبا، ولكن هل يمكن أن تجعل عمل دالة عضو القالب:
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();
}