2009-10-28 21 views
31

का कारण बनती है मुझे सी ++ टेम्पलेट्स के संबंध में क्या हो रहा है, मुझे बहुत कम विचार है, लेकिन मैं एक ऐसे फ़ंक्शन को कार्यान्वित करने की कोशिश कर रहा हूं जो किसी दिए गए प्रॉपर्टी को संतुष्ट करने वाले तत्व के लिए वेक्टर खोजता है (इस मामले में, खोज दिए गए नाम के साथ एक के लिए)।टेम्पलेट समस्या लिंकर त्रुटि (सी ++)

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> >)" ([email protected]@@@@[email protected]@[email protected]@@[email protected]@@@[email protected]@@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@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> >)" ([email protected]@@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@Z) place.obj Program2 

, मैं तो मैं टेम्पलेट्स के लिए नया हूँ पता नहीं क्या चल रहा है। Google के माध्यम से मुझे एलएनके2019 के सभी उदाहरण मिल चुके हैं, सही पुस्तकालयों का उपयोग नहीं कर रहे हैं, लेकिन चूंकि यह मेरा स्वयं का कार्य है, मुझे नहीं लगता कि यह क्यों हो रहा है।

इसके अलावा, एक संबंधित प्रश्न: क्या टेम्पलेट पैरामीटर बनाने का कोई तरीका है ताकि इसे किसी निश्चित वर्ग का उप-वर्ग होना चाहिए, यानी टेम्पलेट?

+0

आप किस कंपाइलर का उपयोग कर रहे हैं? कुछ कंपाइलर्स आपको टेम्पलेट्स के लिए अलग-अलग फाइलों में घोषणा और परिभाषा को अलग करने से रोकते हैं। – Jordan

+0

क्या आपने वास्तव में अपने टेम्पलेट फ़ंक्शन के लिए कार्यान्वयन लिखा था? – begray

+2

आप std :: find या std :: find_if –

उत्तर

59

आपको कॉलिंग साइट पर अपनी टेम्पलेट परिभाषाएं उपलब्ध करनी होंगी। इसका मतलब है .cpp फाइलें।

कारण टेम्पलेट संकलित नहीं किए जा सकते हैं। कुकीज़ के रूप में कार्यों के बारे में सोचें, और संकलक एक ओवन है।

टेम्पलेट्स केवल एक कुकी कटर हैं, क्योंकि वे नहीं जानते कि वे किस प्रकार की कुकी हैं। यह केवल कंपाइलर को बताता है कि कैसे एक प्रकार दिया गया है, लेकिन खुद में, इसका उपयोग नहीं किया जा सकता है क्योंकि कोई ठोस प्रकार संचालित नहीं किया जा रहा है। आप एक कुकी कटर पका नहीं सकते हैं। केवल तभी जब आप स्वादिष्ट कुकी आटा तैयार करते हैं (यानी, कंपाइलर आटा [प्रकार] दिया जाता है)) क्या आप कुकी को काट सकते हैं और इसे पका सकते हैं।

इसी तरह, जब आप वास्तव में किसी निश्चित प्रकार के साथ टेम्पलेट का उपयोग करते हैं तो संकलक वास्तविक फ़ंक्शन उत्पन्न कर सकता है, और इसे संकलित कर सकता है। यह ऐसा नहीं कर सकता है, हालांकि, अगर टेम्पलेट परिभाषा गुम है। आपको इसे हेडर फ़ाइल में ले जाना है, इसलिए फ़ंक्शन का कॉलर कुकी बना सकता है।

+0

धन्यवाद। अब मैं समझता हूं, लेकिन मुझे डर है कि मुझे समझ में नहीं आता कि यह नियमित कार्यों के लिए क्यों कर सकता है लेकिन टेम्पलेट कार्यों के लिए नहीं, जो मुझे लगता है कि मैं सी ++ की पूरी समझ नहीं ले सकता। – marsolk

+0

टेम्पलेट्स बॉयलरप्लेट हैं, कुछ हद तक मैक्रोज़ की तरह, बस बेहतर (और बदतर;)। जब तक आप उनका उपयोग नहीं करते हैं, तब तक संकलक को दिए गए प्रकार (ओं) के साथ मैक्रो-जैसे प्रतिस्थापन करने और वास्तविक फ़ंक्शन बनाने की आवश्यकता नहीं होती है। अब, जब संकलक इस तात्कालिकता को हिट करता है, तो हो सकता है कि इसमें बॉयलरप्लेट हाथ में न हो। यदि आप एक रोडब्लॉक नहीं मारा है। – dirkgently

+1

फ़ंक्शन टेम्पलेट्स * नहीं * फ़ंक्शंस हैं। फंक्शन टेम्पलेट्स * टेम्पलेट्स * हैं। वे अपने नियमों का पालन करते हैं। – AnT

0

क्या आपने अपनी टेम्पलेट फ़ंक्शन परिभाषा को एक सीपीपी फ़ाइल में रखा था? फिर इसे हेडर पर ले जाएं और इसे रेखांकित करें।

+0

नहीं, यह आवश्यक नहीं है। ऊपर चार्ल्स बेली के जवाब देखें। –

29

आप शायद एक वैध तत्कालता खोने से पीड़ित हैं। यदि आप अपनी टेम्पलेट परिभाषा को एक अलग .cpp फ़ाइल में डालते हैं, जब संकलक उस फ़ाइल को संकलित करता है तो यह नहीं पता कि आपको कौन सी तत्काल आवश्यकताएं हैं। इसके विपरीत, कॉल साइटों पर जो टेम्पलेट फ़ंक्शन के सही संस्करण को तुरंत चालू करेंगे, यदि फ़ंक्शन बॉडी की परिभाषा उपलब्ध नहीं है तो संकलक के पास आवश्यक विशेषज्ञता को तुरंत चालू करने की जानकारी नहीं होगी।

आपके पास दो विकल्प हैं। फ़ंक्शन बॉडी को हेडर फ़ाइल में फ़ंक्शन टेम्पलेट के लिए रखें।

उदा। हेडर फ़ाइल में:

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

या स्पष्ट रूप से .cpp में टेम्पलेट को तुरंत चालू करें जहां आपने टेम्पलेट को परिभाषित किया है।

उदा। स्रोत फ़ाइल में (शायद फ़ाइल कि Item को परिभाषित करता है ing #include की आवश्यकता होगी):

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); 
+1

मुझे C++ फ़ाइल में तत्कालता को मजबूर करने के बारे में उस महान युक्ति के कारण स्वीकृत उत्तर से अधिक उपयोगी लगता है। – ancientHacker

9

यहाँ जवाब महान हैं।

मैं बस इतना जोड़ूंगा कि यह अक्सर एक परियोजना में .h और .cpp फ़ाइलों के अतिरिक्त क्यों है। आपको अक्सर .inl फाइलें मिलेंगी। टेम्पलेट परिभाषा .inl फ़ाइल में जाएगी।

ये .inl फाइलों का मतलब इनलाइन है और आमतौर पर सभी शीर्षलेख घोषणाओं के बाद फ़ाइल के निचले हिस्से में उसी नाम उपसर्ग के .h फ़ाइल द्वारा शामिल किया जाएगा। यह प्रभावी रूप से उन्हें हेडर फ़ाइल का हिस्सा बनाता है लेकिन घोषणाओं को किसी भी परिभाषा से अलग करता है।

क्योंकि वे हेडर फाइल महिमा कर रहे हैं आप सभी एक ही सावधानियों कि आप एक नियमित हेडर फाइल के साथ, यानी गार्ड आदि को शामिल किया जाएगा लेना चाहिए

+0

क्या ये '.ll' फ़ाइलें C++ मानक द्वारा समर्थित हैं या वे संकलक-निर्भर हैं? – kim366

0

मैं सिर्फ देखा है कि आप एक दूसरा सवाल जो अनुत्तरित हो रहा है था:

क्या टेम्पलेट पैरामीटर बनाने का कोई तरीका है ताकि इसे किसी निश्चित वर्ग, यानी टेम्पलेट का उप-वर्ग होना चाहिए?

यह संभव है। उदाहरण के लिए, Boost.TypeTraits में is_base_of देखें।

हालांकि, मैं उत्सुक हूं: आप ऐसा क्यों चाहते हैं? आम तौर पर, इसके पैरामीटर पर टेम्पलेट की आवश्यकताएं पैरामीटर के प्रकार पर नहीं होती हैं, लेकिन उस प्रकार से जुड़े अभिव्यक्ति कानूनी हैं। उदाहरण के लिए, कल्पना है कि आप:

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

कह रही है कि टी की तरह कुछ से विरासत चाहिए:

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

कुछ भी नहीं लाता है क्योंकि समारोह के इन्स्टेन्शियशन वैसे भी असफल हो जायेगी अगर प्रकार के संचालन का समर्थन नहीं करता । इसके अलावा, यह foo() की प्रयोज्यता को अनिवार्य रूप से प्रतिबंधित करता है।

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

एक ही मुद्दे पर ठोकर खाई: वास्तव में, foo के किसी भी आवश्यकताओं कि t.foo()and t.bar(const char*) उदाहरण के लिए एक स्थिरांक टी पर मान्य भाव, इस प्रकार HasFooAndBar से विरासत नहीं है और अभी भी एक वैध foo() पैरामीटर है कर रहे हैं और यह पाया गया कि 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; 
} 
+2

मुझे वास्तव में यह विधि पसंद है, लेकिन यह सुनिश्चित करने का कोई तरीका है कि संकलक इस चीज़ को अनुकूलित नहीं करेगा? मैंने एक बार कोशिश की - ओ 3 अनुकूलन, और फिर 'अपरिभाषित प्रतीक' – darwinsenior

संबंधित मुद्दे