تخزين تعريفات دالة قالب C++ في ملف.CPP




templates (7)

المثال الخاص بك هو الصحيح ولكن ليس محمولا للغاية. هناك أيضًا بنية أنظف قليلاً يمكن استخدامها (كما أشار إليها @ namespace-sid).

افترض أن فئة templated جزءًا من بعض المكتبة التي سيتم مشاركتها. هل يجب تجميع إصدارات أخرى من الفصل النموذجي؟ هل يفترض على المشرف على المكتبة توقع جميع الاستخدامات الممكنة للفصل الدراسي؟

النهج البديل هو اختلاف طفيف على ما لديك: إضافة ملف ثالث هو ملف تنفيذ / إنشاء مثيل النموذج.

ملف foo.h

// Standard header file guards omitted

template <typename T>
class foo
{
public:
    void bar(const T& t);
};

ملف foo.cpp

// Always include your headers
#include "foo.h"

template <typename T>
void foo::bar(const T& t)
{
    // Do something with t
}

ملف foo-impl.cpp

// Yes, we include the .cpp file
#include "foo.cpp"
template class foo<int>;

التوضيح الوحيد هو أنك تحتاج إلى إخبار المترجم أن يقوم بتجميع foo-impl.cpp بدلاً من foo.cpp لأن foo.cpp لا يفعل شيئًا.

بالطبع ، يمكن أن يكون لديك عدة تطبيقات في الملف الثالث أو لديك ملفات تنفيذ متعددة لكل نوع ترغب في استخدامه.

يتيح ذلك مرونة أكبر عند مشاركة فئة templated للاستخدامات الأخرى.

يقلل هذا الإعداد أيضًا من أوقات التجميع للفئات المعاد استخدامها نظرًا لأنك لا تعيد ترجمة نفس ملف الرأس في كل وحدة ترجمة.

لدي بعض رمز القالب الذي أفضل تخزينه في ملف CPP بدلاً من المضمن في الرأس. أعلم أنه يمكن القيام بذلك طالما أنك تعرف أنواع القوالب التي سيتم استخدامها. فمثلا:

ملف

class foo
{
public:
    template <typename T>
    void do(const T& t);
};

ملف .cpp

template <typename T>
void foo::do(const T& t)
{
    // Do something with t
}

template void foo::do<int>(const int&);
template void foo::do<std::string>(const std::string&);

لاحظ آخر سطرين - يتم استخدام الدالة foo :: do template فقط مع سلاسل ints و std :: لذلك فإن تلك التعريفات تعني أن التطبيق سوف يرتبط.

سؤالي هو - هل هذا الاختراق سيئ أو هل هذا العمل مع غيرها من compilers / linkers؟ أنا أستخدم هذا الرمز مع VS2008 في الوقت الحالي ، ولكننا سنرغب في الوصول إلى البيئات الأخرى.


حان وقت التحديث! قم بإنشاء ملف inline (.inl أو ربما أي ملف آخر) وقم ببساطة بنسخ كل تعريفاتك فيه. تأكد من إضافة القالب فوق كل وظيفة ( template <typename T, ...> ). الآن بدلاً من تضمين ملف الرأس في الملف المضمن يمكنك القيام بالعكس. قم بتضمين الملف المضمن بعد تعريف الفصل الخاص بك ( #include "file.inl" ).

لا أعرف حقاً لماذا لم يذكر أحد هذا. لا أرى أي عيوب فورية.


للآخرين على هذه الصفحة يتساءل ما هو الصحيح بناء الجملة (كما فعلت I) لتخصص قالب واضح (أو على الأقل في VS2008) ، ما يلي ...

في ملف .h الخاص بك ...

template<typename T>
class foo
{
public:
    void bar(const T &t);
};

وفي ملف .cpp الخاص بك

template <class T>
void foo<T>::bar(const T &t)
{ }

// Explicit template instantiation
template class foo<int>;

نعم ، هذه هي الطريقة المعيارية لإجراء تمييز صريح للتخصيص. كما ذكرت ، لا يمكنك إنشاء هذا القالب بأنواع أخرى.

تعديل: تم تصحيحه استنادًا إلى التعليق.


هذا بالتأكيد ليس اختراقاً سيئاً ، ولكن كن مدركاً لحقيقة أنه سيكون عليك القيام به (تخصص القالب الواضح) لكل فئة / نوع تريد استخدامه مع القالب المحدد. في حالة وجود العديد من الأنواع التي تتطلب إنشاء مثيل في النموذج ، يمكن أن يكون هناك عدد كبير من الخطوط في ملف .cpp الخاص بك. لمعالجة هذه المشكلة ، يمكن أن يكون لديك TemplateClassInst.cpp في كل مشروع تستخدمه بحيث يكون لديك تحكم أكبر في الأنواع التي سيتم إنشاء مثيل لها. ومن الواضح أن هذا الحل لن يكون مثاليا (ويعرف أيضا باسم الرصاصة الفضية) حيث قد ينتهي بك الأمر إلى كسر ODR :).


هذا يجب أن تعمل بشكل جيد في كل مكان يتم دعم قوالب. يعد إنشاء مثيل قالب صريح جزءًا من C ++ القياسية.


يمكن حل المشكلة التي تصفها عن طريق تعريف القالب في الرأس ، أو عن طريق النهج الموضح أعلاه.

أوصي بقراءة النقاط التالية من C ++ FAQ Lite :

يذهبون إلى الكثير من التفاصيل حول هذه القوالب (وغيرها) القضايا.





templates