سؤال

ليس لدي أي فكرة عما يحدث فيما يتعلق بقوالب C++، ولكني أحاول تنفيذ وظيفة تبحث عن متجه لعنصر يلبي خاصية معينة (في هذه الحالة، البحث عن واحد بالاسم المحدد).إعلاني في ملف .h الخاص بي هو كما يلي:

template <typename T>
T* find_name(std::vector<T*> v, std::string name);

عندما أقوم بالتجميع، أحصل على خطأ الرابط هذا عندما أقوم باستدعاء الوظيفة:

Error   1   error LNK2019: unresolved external symbol "class Item * __cdecl find_name<class Item>(class std::vector<class Item *,class std::allocator<class Item *> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (??$find_name@VItem@@@@YAPAVItem@@V?$vector@PAVItem@@V?$allocator@PAVItem@@@std@@@std@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z) referenced in function "public: class Item * __thiscall Place::get_item(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" (?get_item@Place@@QAEPAVItem@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) place.obj   Program2

مرة أخرى، أنا جديد في استخدام القوالب، لذا لا أعرف ما الذي سيحدث.جميع الأمثلة التي وجدتها عن LNK2019 من خلال Google كانت تتعلق بعدم استخدام المكتبات الصحيحة، ولكن بما أن هذه هي وظيفتي الخاصة، فلا أرى سبب حدوث ذلك.

سؤال ذو صلة أيضًا:هل هناك طريقة لإنشاء معلمة قالب بحيث يجب أن تكون فئة فرعية لفئة معينة، على سبيل المثال.نموذج؟

هل كانت مفيدة؟

المحلول

يجب أن تكون تعريفات القالب الخاصة بك متاحة في موقع الاتصال.وهذا يعني لا .cpp ملفات.

والسبب هو أنه لا يمكن تجميع القوالب.فكر في الوظائف مثل ملفات تعريف الارتباط، والمترجم عبارة عن فرن.

القوالب هي مجرد أداة لقطع ملفات تعريف الارتباط، لأنها لا تعرف نوع ملف تعريف الارتباط.إنه يخبر المترجم فقط بكيفية إنشاء الوظيفة عند إعطاء نوع، ولكن في حد ذاته، لا يمكن استخدامه لأنه لا يوجد نوع ملموس يتم تشغيله عليه.لا يمكنك طهي قطع الكعكة.فقط عندما تكون عجينة البسكويت اللذيذة جاهزة (على سبيل المثال، بالنظر إلى المترجم العجين [نوع])) يمكنك قطع البسكويت وطهيه.

وبالمثل، فقط عندما تستخدم القالب فعليًا بنوع معين، يمكن للمترجم إنشاء الوظيفة الفعلية وتجميعها.ومع ذلك، لا يمكنه القيام بذلك، إذا كان تعريف القالب مفقودًا.يجب عليك نقله إلى ملف الرأس، حتى يتمكن المتصل بالوظيفة من إنشاء ملف تعريف الارتباط.

نصائح أخرى

من المحتمل أنك تعاني من فقدان إنشاء مثيل صالح.إذا قمت بوضع تعريف القالب الخاص بك في ملف .cpp منفصل، فعندما يقوم المترجم بترجمة هذا الملف، فقد لا يعرف عمليات المثيل التي تحتاجها.على العكس من ذلك، في مواقع الاتصال التي من شأنها إنشاء نسخة صحيحة من وظيفة القالب، إذا لم يكن تعريف نص الوظيفة متاحًا، فلن يكون لدى المترجم المعلومات اللازمة لإنشاء مثيل للتخصصات المطلوبة.

لديك خياران.ضع نص الوظيفة لقالب الوظيفة في ملف الرأس.

على سبيل المثالفي ملف الرأس:

template <typename T>
inline T* find_name(std::vector<T*> v, std::string name)
{
    // ...
}

أو قم بإنشاء مثيل للقالب بشكل صريح في ملف .cpp حيث قمت بتحديد القالب.

على سبيل المثالفي الملف المصدر (ربما سيتطلب ذلك #includeفي الملف الذي يحدد Item):

template <typename T>
T* find_name(std::vector<T*> v, std::string name)
{
    // ...
}

template Item* find_name<Item>(std::vector<Item*> v, std::string name);

الإجابات هنا رائعة.

سأضيف فقط أن هذا هو السبب بالإضافة إلى ذلك .h و .cpp الملفات في المشروع.ستجد في كثير من الأحيان .inl ملفات.سوف تدخل تعريفات القالب في ملف .inl ملف.

هؤلاء .inl الملفات تعني مضمنة وعادة ما يتم تضمينها بواسطة .h ملف يحمل نفس الاسم البادئة في أسفل الملف بعد كل إعلانات الرأس.وهذا يجعلها جزءًا من ملف الرأس بشكل فعال ولكنها تفصل الإعلانات عن أي تعريفات.

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

تعثرت في نفس المشكلة ووجدت هذا الذي ينص على 3 حلول:http://www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-a-h-file-and-imp

من بينها طريقة سهلة حيث تقوم بإنشاء طريقة "وهمية" في ملف .cpp، والتي تستدعي وظيفة القالب/الفئة بأنواع مختلفة.تم إلصاقها من الرابط:

// No need to call this TemporaryFunction() function, it's just to avoid link error.
void TemporaryFunction ()
{
    TestTemp<int> TempObj;
    TestTemp<float> TempObj2;
}

لقد لاحظت للتو أن لديك سؤالًا ثانيًا يبدو أنه بدون إجابة:

هل هناك طريقة لإنشاء معلمة قالب بحيث يجب أن تكون فئة فرعية لفئة معينة، على سبيل المثال.نموذج؟

إنه ممكن.على سبيل المثال، انظر is_base_of في Boost.TypeTraits.

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

template<class T>
void foo(const T& t)
{
    if (t.foo()){
       t.bar("blah");
    }
}

القول بأن T يجب أن يرث من شيء مثل:

class HasFooAndBar
{
public:
  void foo()const;
  void bar(const char*)const;
};

لا يجلب شيئًا لأن إنشاء مثيل للوظيفة سيفشل على أي حال إذا كان النوع لا يدعم العمليات.علاوة على ذلك، فهو يقيد دون داع إمكانية تطبيقه foo().في الواقع، foo's أي متطلبات هي ذلك t.foo() and t.bar(const char*) تعبيرات صالحة على const T.على سبيل المثال، هذا النوع لا يرث من HasFooAndBar ولا يزال معلمة foo() صالحة:

struct DifferentFromHasFooAndBar
{
  bool foo()const;
  std::string bar(const std::string&)const;
};

هل وضعت تعريف وظيفة القالب الخاص بك في ملف CPP؟ثم انقله إلى الرأس وقم بتضمينه.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top