टेम्प्लेट को हेडर फ़ाइल में ही क्यों लागू किया जा सकता है?


1775

कोट फ़ॉर्म C ++ मानक लाइब्रेरी : एक ट्यूटोरियल और हैंडबुक :

फिलहाल टेम्पलेट का उपयोग करने का एकमात्र पोर्टेबल तरीका इनलाइन कार्यों का उपयोग करके हेडर फ़ाइलों में उन्हें लागू करना है।

ऐसा क्यों है?

(स्पष्टीकरण: हेडर फाइलें केवल पोर्टेबल समाधान नहीं हैं। लेकिन वे सबसे सुविधाजनक पोर्टेबल समाधान हैं।)


13
हालांकि यह सच है कि हेडर फ़ाइल में सभी टेम्प्लेट फ़ंक्शन परिभाषाओं को रखना शायद उनका उपयोग करने का सबसे सुविधाजनक तरीका है, यह अभी भी स्पष्ट नहीं है कि उस उद्धरण में "इनलाइन" क्या है। उसके लिए इनलाइन फ़ंक्शंस का उपयोग करने की कोई आवश्यकता नहीं है। "इनलाइन" का इससे कोई लेना-देना नहीं है।
एनटी

7
पुस्तक पुराना है।
gerardw

1
एक टेम्पलेट एक फ़ंक्शन की तरह नहीं है जिसे बाइट कोड में संकलित किया जा सकता है। यह इस तरह के एक समारोह उत्पन्न करने के लिए सिर्फ एक पैटर्न है। यदि आप अपने आप में एक .cpp फ़ाइल में एक टेम्पलेट डालते हैं, तो संकलन करने के लिए कुछ भी नहीं है। इसके अलावा, एक्सप्लिसिट इंस्टेंटेशन वास्तव में एक टेम्प्लेट नहीं है, लेकिन टेम्पलेट से एक फ़ंक्शन बनाने के लिए प्रारंभिक बिंदु जो * .obj फ़ाइल में समाप्त होता है।
1919

5
क्या मैं केवल वह हूं जो महसूस करता है कि टेम्पलेट अवधारणा इसके कारण C ++ में अपंग है? ...
DragonGamer

जवाबों:


1556

कैविएट: कार्यान्वयन को हेडर फ़ाइल में रखना आवश्यक नहीं है, इस उत्तर के अंत में वैकल्पिक समाधान देखें।

वैसे भी, आपके कोड के विफल होने का कारण यह है कि, जब किसी टेम्प्लेट को तत्काल किया जाता है, तो कंपाइलर दिए गए टेम्प्लेट तर्क के साथ एक नया वर्ग बनाता है। उदाहरण के लिए:

template<typename T>
struct Foo
{
    T bar;
    void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo<int> f; 

इस पंक्ति को पढ़ते समय, कंपाइलर एक नया वर्ग बनाएगा (इसे कॉल करें FooInt), जो निम्न के बराबर है:

struct FooInt
{
    int bar;
    void doSomething(int param) {/* do stuff using int */}
}

नतीजतन, संकलक को विधियों के कार्यान्वयन तक पहुंच की आवश्यकता है, उन्हें टेम्पलेट तर्क (इस मामले में int) के साथ त्वरित करने के लिए । यदि ये कार्यान्वयन हेडर में नहीं थे, तो वे सुलभ नहीं होंगे, और इसलिए कंपाइलर टेम्पलेट को इंस्टेंट नहीं कर पाएगा।

इसका एक सामान्य समाधान हेडर फ़ाइल में टेम्प्लेट डिक्लेरेशन लिखना है, फिर क्लास को कार्यान्वयन फ़ाइल (उदाहरण के लिए .tpp) में लागू करें, और हेडर के अंत में इस कार्यान्वयन फ़ाइल को शामिल करें।

Foo.h

template <typename T>
struct Foo
{
    void doSomething(T param);
};

#include "Foo.tpp"

Foo.tpp

template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}

इस तरह, कार्यान्वयन अभी भी घोषणा से अलग है, लेकिन संकलक के लिए सुलभ है।

दूसरा तरीका

एक अन्य उपाय कार्यान्वयन को अलग रखना है, और आपके द्वारा आवश्यक सभी टेम्पलेट उदाहरणों को स्पष्ट रूप से तत्काल करना है:

Foo.h

// no implementation
template <typename T> struct Foo { ... };

Foo.cpp

// implementation of Foo's methods

// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float

यदि मेरी व्याख्या पर्याप्त नहीं है, तो आप इस विषय पर C ++ सुपर-एफएक्यू पर एक नज़र डाल सकते हैं ।


96
वास्तव में स्पष्ट तात्कालिकता को एक .cpp फ़ाइल में होना चाहिए जिसकी हेडर के बजाय फू के सभी सदस्य कार्यों की परिभाषाओं तक पहुंच हो।
मकारसे

11
"कंपाइलर को टेम्प्लेट तर्क (इस मामले में int) के साथ त्वरित करने के लिए, विधियों के कार्यान्वयन तक पहुंच की आवश्यकता होती है। यदि ये कार्यान्वयन हेडर में नहीं थे, तो वे सुलभ नहीं होंगे" लेकिन कार्यान्वयन में क्यों है .cpp फ़ाइल संकलक के लिए सुलभ नहीं है? एक संकलक भी .cpp जानकारी तक पहुंच सकता है, यह उन्हें .obj फ़ाइलों में और कैसे बदल देगा? EDIT: इस प्रश्न का उत्तर इस उत्तर में दिए गए लिंक में है ...
xcrypt

31
मुझे नहीं लगता कि यह प्रश्न स्पष्ट रूप से बताता है, महत्वपूर्ण बात स्पष्ट रूप से संकलन
यूआईआईटी

6
@ गैबसन: संरचनाएं और कक्षाएं इस अपवाद के बराबर हैं कि कक्षाओं के लिए डिफ़ॉल्ट पहुंच संशोधक "निजी" है, जबकि यह संरचनाओं के लिए सार्वजनिक है। कुछ अन्य छोटे अंतर हैं जो आप इस प्रश्न को देखकर सीख सकते हैं ।
ल्यूक टॉरिल

3
मैंने इस उत्तर के प्रारंभ में एक वाक्य जोड़ा है कि यह स्पष्ट करने के लिए कि प्रश्न झूठे आधार पर आधारित है। अगर कोई पूछता है कि "एक्स सच क्यों है?" जब वास्तव में X सत्य नहीं होता है, तो हमें उस धारणा को जल्दी से अस्वीकार कर देना चाहिए।
हारून मैकडैड

250

यहाँ बहुत सही उत्तर हैं, लेकिन मैं इसे (पूर्णता के लिए) जोड़ना चाहता था:

यदि आप, कार्यान्वयन cpp फ़ाइल के निचले भाग में, उन सभी प्रकारों का स्पष्ट रूप से त्वरित रूप से उपयोग करते हैं, जिनके साथ टेम्पलेट का उपयोग किया जाएगा, तो लिंकर उन्हें हमेशा की तरह ढूंढ सकेगा।

संपादित करें: स्पष्ट टेम्पलेट तात्कालिकता का उदाहरण जोड़ना। टेम्प्लेट को परिभाषित करने के बाद उपयोग किया जाता है, और सभी सदस्य कार्यों को परिभाषित किया गया है।

template class vector<int>;

यह तुरंत (और इस तरह लिंकर को उपलब्ध कराएगा) वर्ग और उसके सभी सदस्य कार्य (केवल)। टेम्पलेट फ़ंक्शन के लिए समान सिंटैक्स काम करता है, इसलिए यदि आपके पास गैर-सदस्य ऑपरेटर ओवरलोड हैं, तो आपको उन लोगों के लिए भी ऐसा करने की आवश्यकता हो सकती है।

उपरोक्त उदाहरण काफी बेकार है क्योंकि वेक्टर एक हेडर में पूरी तरह से परिभाषित किया गया है, सिवाय जब एक आम फ़ाइल (precompiled कोडेक) का उपयोग करता है, extern template class vector<int>तो इसका उपयोग सभी अन्य (1000?) फ़ाइलों में वेक्टर को उपयोग करने से रोकने के लिए किया जाता है।


51
ओह। अच्छा जवाब है, लेकिन कोई वास्तविक साफ समाधान नहीं है। टेम्प्लेट के लिए सभी संभावित प्रकारों को सूचीबद्ध करना एक टेम्प्लेट माना नहीं जाता है।

6
यह कई मामलों में अच्छा हो सकता है, लेकिन आम तौर पर टेम्पलेट के उद्देश्य को तोड़ता है जो आपको typeमैन्युअल रूप से सूचीबद्ध किए बिना किसी भी वर्ग के साथ उपयोग करने की अनुमति देता है।
टॉम ज़ातो - मोनिका

7
vectorएक अच्छा उदाहरण नहीं है क्योंकि एक कंटेनर स्वाभाविक रूप से "सभी" प्रकारों को लक्षित कर रहा है। लेकिन यह बहुत बार होता है कि आप ऐसे टेम्पलेट बनाते हैं जो केवल विशिष्ट प्रकार के सेट के लिए होते हैं, उदाहरण के लिए संख्यात्मक प्रकार: int8_t, int16_t, int32_t, uint8_t, uint16_t, आदि। इस मामले में, यह अभी भी एक टेम्पलेट का उपयोग करने के लिए समझ में आता है। , लेकिन स्पष्ट रूप से प्रकार के पूरे सेट के लिए उन्हें तत्काल करना भी संभव है और, मेरी राय में, अनुशंसित है।
अंकलजीव

टेम्प्लेट को परिभाषित करने के बाद उपयोग किया जाता है, "और सभी सदस्य कार्यों को परिभाषित किया गया है"। धन्यवाद !
Vitt Volt

1
मुझे ऐसा लगता है कि मुझे कुछ याद आ रहा है ... मैंने दो प्रकारों के लिए स्पष्ट तात्कालिकता को कक्षा की .cppफ़ाइल में डाल दिया है और दो तात्कालिकताएँ अन्य .cppफ़ाइलों से संदर्भित हैं , और मुझे अभी भी लिंकिंग त्रुटि मिलती है जो सदस्यों को नहीं मिली है।
ओराफिश

250

यह अलग संकलन की आवश्यकता के कारण है और क्योंकि तात्कालिकता-शैली बहुरूपता हैं।

स्पष्टीकरण के लिए कंक्रीट के करीब थोड़ा मिलता है। कहो कि मुझे निम्नलिखित फाइलें मिली हैं:

  • foo.h
    • का इंटरफ़ेस घोषित करता है class MyClass<T>
  • foo.cpp
    • के कार्यान्वयन को परिभाषित करता है class MyClass<T>
  • bar.cpp
    • का उपयोग करता है MyClass<int>

अलग संकलन साधन मैं संकलित करने के लिए सक्षम होना चाहिए foo.cpp से स्वतंत्र रूप से bar.cpp । कंपाइलर पूरी तरह से स्वतंत्र रूप से प्रत्येक संकलन इकाई पर विश्लेषण, अनुकूलन और कोड पीढ़ी के सभी कड़ी मेहनत करता है; हमें पूरे कार्यक्रम का विश्लेषण करने की आवश्यकता नहीं है। यह केवल लिंकर है जिसे एक बार में पूरे कार्यक्रम को संभालने की आवश्यकता है, और लिंकर का काम काफी आसान है।

bar.cpp भी मौजूद करने के लिए जब मैं संकलन की जरूरत नहीं है foo.cpp , लेकिन मैं अभी भी लिंक करने के लिए सक्षम होना चाहिए foo.o मैं पहले से ही के साथ मिलकर किया था bar.o कंपाइल करने के लिए की जरूरत के बिना, मैं सिर्फ उत्पादन किया गया है foo सीपीपीfoo.cpp को एक गतिशील लाइब्रेरी में भी संकलित किया जा सकता है, जिसे foo.cpp के बिना कहीं और वितरित किया गया है , और कोड के साथ जुड़ा हुआ है जो मैंने foo.cpp लिखा था

"तात्कालिकता-शैली बहुरूपता" का मतलब है कि टेम्पलेट MyClass<T>वास्तव में एक सामान्य वर्ग नहीं है जिसे कोड के लिए संकलित किया जा सकता है जो किसी भी मूल्य के लिए काम कर सकता है T। यही कारण है कि मुक्केबाजी के रूप में भूमि के ऊपर इस तरह जोड़ना होगा, allocators और कंस्ट्रक्टर, आदि सी ++ टेम्पलेट्स के इरादे लिखने के लिए होने से बचने के लिए है करने के लिए समारोह संकेत पारित करने के लिए की आवश्यकता होगी, लगभग समान class MyClass_int, class MyClass_float, आदि, लेकिन अभी भी संकलित कोड है कि अंत करने के लिए सक्षम होने के लिए जैसे कि हमने प्रत्येक संस्करण को अलग से लिखा था । तो एक टेम्पलेट सचमुच एक टेम्पलेट है; एक वर्ग टेम्पलेट एक वर्ग नहीं है, यह Tहमारे द्वारा सामना किए जाने वाले प्रत्येक के लिए एक नया वर्ग बनाने के लिए एक नुस्खा है । एक टेम्पलेट को कोड में संकलित नहीं किया जा सकता है, केवल टेम्पलेट को त्वरित करने का परिणाम संकलित किया जा सकता है।

इसलिए जब foo.cpp संकलित किया जाता है, तो संकलक बार.कंप को यह जानने के लिए नहीं देख सकता है कि MyClass<int>इसकी आवश्यकता है। यह टेम्प्लेट देख सकता है MyClass<T>, लेकिन यह उसके लिए कोड का उत्सर्जन नहीं कर सकता (यह एक टेम्प्लेट है, क्लास नहीं)। और जब bar.cpp संकलित किया जाता है, तो संकलक यह देख सकता है कि इसे a बनाने की आवश्यकता है MyClass<int>, लेकिन यह टेम्पलेट MyClass<T>( foo.h में केवल इसका इंटरफ़ेस नहीं देख सकता है ) ) इसलिए इसे नहीं बना सकता है।

यदि foo.cpp खुद का उपयोग करता है MyClass<int>, तो उस के लिए कोड, जबकि संकलन उत्पन्न हो जाएगा foo.cpp , इसलिए जब bar.o से जुड़ा हुआ है foo.o वे झुका जा सकता है और काम करेंगे। हम उस तथ्य का उपयोग कर सकते हैं कि एक ही टेम्पलेट लिखकर .cpp फ़ाइल में कार्यान्वित होने के लिए टेम्पलेट तात्कालिकता के एक सीमित सेट की अनुमति दें। लेकिन बारकोप के लिए टेम्पलेट के रूप में टेम्पलेट का उपयोग करने और जो भी इसे पसंद है उस पर इसे तुरंत करने का कोई तरीका नहीं है ; यह केवल अस्थायी वर्ग के पूर्व-मौजूदा संस्करणों का उपयोग कर सकता है जो foo.cpp के लेखक हैं प्रदान करने के लिए सोचा था।

आप सोच सकते हैं कि जब एक टेम्पलेट को संकलित किया जाता है तो संकलक को "सभी संस्करण उत्पन्न करना चाहिए", उन लोगों के साथ जो लिंकिंग के दौरान कभी भी फ़िल्टर नहीं किए जाते हैं। भारी ओवरहेड और चरम कठिनाइयों के अलावा इस तरह के एक दृष्टिकोण का सामना करना पड़ेगा क्योंकि "टाइप संशोधक" जैसे संकेत और सरणियाँ जैसी सुविधाएँ यहां तक ​​कि अंतर्निहित प्रकारों को भी अनंत प्रकार की संख्या देने की अनुमति देती हैं, जब मैं अब अपना कार्यक्रम बढ़ाता हूं तो क्या होता है जोड़ कर:

  • baz.cpp
    • घोषित करता है और लागू class BazPrivateकरता है, और उपयोग करता हैMyClass<BazPrivate>

कोई भी संभव तरीका नहीं है कि यह तब तक काम कर सकता है जब तक कि हम या तो

  1. हर बार जब हम कोई नया फ़ाइल प्रोग्राम में जोड़ते हैं, तो foo.cpp को फिर से शुरू करेंMyClass<T>
  2. आवश्यकता है कि baz.cpp सम्‍मिलित है (संभवत: हेडर के माध्‍यम से) इसमें पूर्ण टेम्पलेट शामिल है MyClass<T>, ताकि कंपाइलर bc.cpp केMyClass<BazPrivate> संकलन के दौरान उत्पन्न हो सके

किसी को पसंद नहीं है (1), क्योंकि पूरे-प्रोग्राम-विश्लेषण संकलन सिस्टम हमेशा के लिए संकलन करने के लिए लेते हैं , और क्योंकि यह स्रोत कोड के बिना संकलित पुस्तकालयों को वितरित करना असंभव बनाता है। इसलिए हमारे पास (2) है।


50
जोर दिया उद्धरण एक टेम्पलेट सचमुच एक टेम्पलेट है; एक क्लास टेम्प्लेट एक क्लास नहीं है, यह प्रत्येक टी के लिए एक नई क्लास बनाने की एक रेसिपी है
जिसका

मैं जानना चाहता हूं कि क्या क्लास के हेडर या सोर्स फाइल के अलावा कहीं और से इंस्टेंट इंस्टिट्यूशन करना संभव है? उदाहरण के लिए, उन्हें main.cpp में करें?
gromit190

1
@Birger आपको किसी भी फ़ाइल से ऐसा करने में सक्षम होना चाहिए जिसमें पूर्ण टेम्पलेट कार्यान्वयन तक पहुंच हो (या तो क्योंकि यह उसी फ़ाइल में है या हेडर के माध्यम से शामिल है)।
बेन

11
@ हाजी यह बयानबाजी नहीं है। सवाल यह है कि "आपको हेडर में टेम्प्लेट क्यों लागू करना है?", इसलिए मैंने तकनीकी विकल्पों को समझाया कि सी ++ भाषा इस आवश्यकता को पूरा करती है। इससे पहले कि मैं अपना जवाब लिखता दूसरों को पहले से ही उपलब्ध कराए गए समाधान हैं जो पूर्ण समाधान नहीं हैं, क्योंकि पूर्ण समाधान नहीं हो सकता है। मुझे लगा कि उन उत्तरों को प्रश्न के "क्यों" कोण की एक फुलर चर्चा द्वारा पूरित किया जाएगा।
बेन

1
इस तरह से कल्पना करें कि लोग ... यदि आप टेम्प्लेट का उपयोग नहीं कर रहे थे (कुशलता से आपको जो चाहिए उसे कोड करने के लिए), तो आप केवल उस वर्ग के कुछ संस्करणों की पेशकश करेंगे। तो आपके पास 3 विकल्प हैं। 1)। टेम्पलेट्स का उपयोग न करें। (सभी अन्य वर्गों / कार्यों की तरह, किसी को भी परवाह नहीं है कि अन्य प्रकार को बदल नहीं सकते हैं) 2)। टेम्प्लेट, और दस्तावेज़ का उपयोग करें कि वे किस प्रकार का उपयोग कर सकते हैं। 3)। उन्हें संपूर्ण कार्यान्वयन (स्रोत) बोनस 4) दें। ); उन्हें पूरी स्रोत मामले में वे अपनी कक्षाओं में से एक से एक खाका बनाना चाहते देना
डबरा

81

टेम्पलेट को वास्तव में ऑब्जेक्ट कोड में संकलित करने से पहले संकलक द्वारा तत्काल किया जाना चाहिए । यह तात्कालिकता केवल तभी प्राप्त की जा सकती है जब टेम्पलेट तर्क ज्ञात हों। अब एक ऐसे परिदृश्य की कल्पना करें जहां एक टेम्पलेट फ़ंक्शन घोषित किया गया है a.h, जिसमें परिभाषित किया गया है a.cppऔर इसका उपयोग किया जाता है b.cpp। जब a.cppसंकलित किया जाता है, तो यह आवश्यक रूप से ज्ञात नहीं है कि आगामी संकलन b.cppको टेम्पलेट के एक उदाहरण की आवश्यकता होगी, अकेले चलो जो विशिष्ट उदाहरण होगा। अधिक हेडर और स्रोत फ़ाइलों के लिए, स्थिति जल्दी से अधिक जटिल हो सकती है।

एक तर्क दे सकता है कि टेम्पलेट के सभी उपयोगों के लिए कंपाइलर्स को "आगे देखने" के लिए स्मार्ट बनाया जा सकता है, लेकिन मुझे यकीन है कि पुनरावर्ती या अन्यथा जटिल परिदृश्य बनाना मुश्किल नहीं होगा। AFAIK, संकलक ऐसे नहीं दिखते आहियाँ। जैसा कि एंटोन ने बताया, कुछ संकलक टेम्पलेट तात्कालिकता के स्पष्ट निर्यात घोषणाओं का समर्थन करते हैं, लेकिन सभी संकलक इसका (अभी तक) समर्थन नहीं करते हैं।


1
"निर्यात" मानक है, लेकिन अभी तक संकलक टीमों में से अधिकांश को लागू करना कठिन है।
वाव

5
निर्यात स्रोत के प्रकटीकरण की आवश्यकता को समाप्त नहीं करता है, और न ही यह संकलन निर्भरता को कम करता है, जबकि इसे कंपाइलर बिल्डरों से बड़े पैमाने पर प्रयास की आवश्यकता होती है। इसलिए हर्ब सटर ने खुद कंपाइलर बिल्डरों को 'निर्यात के बारे में भूलने' के लिए कहा। जैसे-जैसे समय की आवश्यकता होगी निवेश कहीं और बेहतर होगा ...
पीटर

2
इसलिए मुझे नहीं लगता कि निर्यात 'अभी तक' लागू नहीं हुआ है। यह शायद ईडीजी के अलावा किसी और के द्वारा कभी नहीं किया जाएगा, क्योंकि दूसरों ने देखा कि इसमें कितना समय लगा, और कितना कम प्राप्त हुआ
पीटर

3
यदि आपकी रुचि है, तो पेपर को "हम निर्यात क्यों नहीं कर सकते हैं" कहा जाता है, यह उसके ब्लॉग ( gotw.ca/publications ) पर सूचीबद्ध है, लेकिन वहां कोई पीडीएफ नहीं है (एक त्वरित Google को हालांकि इसे चालू करना चाहिए)
पीटर

1
ठीक है, अच्छे उदाहरण और स्पष्टीकरण के लिए धन्यवाद। हालांकि मेरा सवाल यह है: संकलक यह पता नहीं लगा सकता है कि टेम्पलेट को कहाँ कहा गया है, और परिभाषा फ़ाइल संकलित करने से पहले उन फ़ाइलों को संकलित करें? मैं कल्पना कर सकता हूं कि यह एक साधारण मामले में किया जा सकता है ... क्या जवाब है कि अन्योन्याश्रितियां बहुत तेजी से आदेश को गड़बड़ कर देंगी?
व्लाद

62

दरअसल, C ++ 11 से पहले मानक ने उस exportकीवर्ड को परिभाषित किया था जो होगा है कि यह एक हेडर फाइल में टेम्पलेट्स घोषित करने के लिए संभव बनाते हैं और उन्हें कहीं और लागू।

किसी भी लोकप्रिय कंपाइलर ने इस कीवर्ड को लागू नहीं किया। केवल एक ही मुझे पता है कि एडिसन डिज़ाइन समूह द्वारा लिखा गया दृश्य है, जिसका उपयोग कोमू सी ++ कंपाइलर द्वारा किया जाता है। अन्य सभी को आपको हेडर फ़ाइलों में टेम्प्लेट लिखने की आवश्यकता होती है, क्योंकि कंपाइलर को उचित तात्कालिकता के लिए टेम्प्लेट की परिभाषा की आवश्यकता होती है (जैसा कि अन्य पहले ही बता चुके हैं)।

नतीजतन, आईएसओ सी ++ मानक समिति ने exportसी ++ 11 के साथ टेम्पलेट्स की सुविधा को हटाने का फैसला किया ।


6
... और कुछ साल बाद, मैं आखिरकार समझ गया कि exportवास्तव में हमें क्या दिया गया है, और क्या नहीं ... और अब मैं पूरे दिल से EDG लोगों से सहमत हूं: यह हमें नहीं लाया होगा जो ज्यादातर लोग ('11 में खुद) शामिल) लगता है कि यह होगा, और सी ++ मानक इसके बिना बेहतर है।
DevSolar

4
@DevSolar: यह पेपर राजनीतिक, दोहराव और बुरी तरह से लिखा गया है। यह सामान्य मानक स्तर का गद्य नहीं है। गैरकानूनी रूप से लंबे और उबाऊ, मूल रूप से एक ही बात कह रहे हैं कि दसियों पृष्ठों के बराबर। लेकिन मुझे अब सूचित किया जाता है कि निर्यात निर्यात नहीं है। यह एक अच्छा इंटेल है!
v.oddou

1
@ v.oddou: अच्छे डेवलपर और अच्छे तकनीकी लेखक दो अलग कौशल हैं। कुछ दोनों कर सकते हैं, कई नहीं कर सकते। ;-)
DevSolar

@ v.oddou कागज केवल बुरी तरह से लिखा नहीं है, यह कीटाणुशोधन है। यह भी वास्तविकता पर एक स्पिन है: निर्यात के लिए वास्तव में बेहद मजबूत तर्क क्या हैं, यह एक तरह से मिश्रित होता है जैसे कि वे निर्यात के खिलाफ होते हैं: "निर्यात की उपस्थिति में मानक में कई छिद्रित छेदों की खोज करना। निर्यात से पहले, ओडीआर उल्लंघन का संकलक द्वारा निदान नहीं किया जाना था। अब यह आवश्यक है क्योंकि आपको विभिन्न अनुवाद इकाइयों से आंतरिक डेटा संरचनाओं को संयोजित करने की आवश्यकता है, और यदि आप वास्तव में विभिन्न चीजों का प्रतिनिधित्व कर रहे हैं तो आप उन्हें संयोजित नहीं कर सकते हैं, इसलिए आपको चेकिंग करने की आवश्यकता है। "
क्यूरियसगुए

" अब यह जोड़ना होगा कि यह किस अनुवाद इकाई में हुआ था " डुह। जब आप उस लंगड़े तर्क का उपयोग करने के लिए मजबूर होते हैं, तो आपके पास कोई तर्क नहीं होता है। बेशक आप अपनी त्रुटियों में फ़ाइल नामों का उल्लेख करने जा रहे हैं, क्या सौदा है? यह किसी के लिए है कि बीएस मनमौजी है। " जेम्स कान्जे जैसे विशेषज्ञों को भी यह स्वीकार करना कठिन है कि निर्यात वास्तव में ऐसा है। " क्या? !!!!
१६

34

हालाँकि मानक C ++ की ऐसी कोई आवश्यकता नहीं है, कुछ कंपाइलरों को यह आवश्यक है कि सभी फ़ंक्शन और क्लास टेम्प्लेट्स को उनके द्वारा उपयोग की जाने वाली प्रत्येक अनुवाद इकाई में उपलब्ध कराने की आवश्यकता हो। वास्तव में, उन कंपाइलरों के लिए, टेम्पलेट फ़ंक्शंस की बॉडी को हेडर फ़ाइल में उपलब्ध कराया जाना चाहिए। दोहराने के लिए: इसका मतलब है कि वे संकलक उन्हें गैर-हेडर फ़ाइलों में परिभाषित करने की अनुमति नहीं देंगे जैसे कि .cpp फाइलें

एक निर्यात कीवर्ड है जो इस समस्या को कम करने वाला है, लेकिन यह पोर्टेबल होने के करीब नहीं है।


मैं उन्हें "इनलाइन" कीवर्ड के साथ .cpp फ़ाइल में लागू क्यों नहीं कर सकता?
मेनआईडी

2
आप कर सकते हैं, और आपको "इनलाइन" भी नहीं डालना है। लेकिन आप उन्हें केवल उस cpp फ़ाइल में और कहीं और उपयोग कर पाएंगे।
वाव

10
यह लगभग सबसे सटीक उत्तर है, "सिवाय इसका मतलब है कि उन संकलक उन्हें गैर-हेडर फ़ाइलों में परिभाषित करने की अनुमति नहीं देंगे। जैसे कि .cpp फाइलें" वर्तमान में गलत है।
ऑर्बिट

28

हेडर में टेम्प्लेट का उपयोग किया जाना चाहिए क्योंकि कंपाइलर को कोड के विभिन्न संस्करणों को तत्काल करने की आवश्यकता होती है, जो कि टेम्प्लेट पैरामीटर के लिए दिए गए / घटाए गए मापदंडों के आधार पर होता है। याद रखें कि एक टेम्पलेट सीधे कोड का प्रतिनिधित्व नहीं करता है, लेकिन उस कोड के कई संस्करणों के लिए एक टेम्पलेट। जब आप एक गैर-टेम्पलेट फ़ंक्शन को संकलित करते हैं.cpp फ़ाइल संकलित करते हैं, तो आप एक ठोस फ़ंक्शन / क्लास संकलित कर रहे हैं। यह टेम्प्लेट के लिए मामला नहीं है, जिसे विभिन्न प्रकारों के साथ त्वरित किया जा सकता है, अर्थात्, ठोस प्रकार के साथ टेम्पलेट मापदंडों को प्रतिस्थापित करते समय ठोस कोड को उत्सर्जित किया जाना चाहिए।

exportकीवर्ड के साथ एक सुविधा थी जिसका उपयोग अलग संकलन के लिए किया जाना था। इस exportसुविधा को C++11AFAIK में चित्रित किया गया है, केवल एक कंपाइलर ने इसे लागू किया है। आप का उपयोग नहीं करना चाहिए export। अलग संकलन में संभव नहीं है C++या C++11लेकिन शायद मेंC++17 , अगर अवधारणाओं में आने से पहले, हम अलग संकलन की किसी तरह से हो सकता था।

प्राप्त करने के लिए अलग संकलन के लिए, अलग टेम्पलेट बॉडी जाँच संभव होना चाहिए। ऐसा लगता है कि अवधारणाओं के साथ एक समाधान संभव है। हाल ही में मानकों के अनुरूप बैठक में प्रस्तुत इस पत्र पर एक नज़र डालें । मुझे लगता है कि यह एकमात्र आवश्यकता नहीं है, क्योंकि आपको अभी भी उपयोगकर्ता कोड में टेम्पलेट कोड के लिए तत्काल कोड की आवश्यकता है।

टेम्पलेट्स के लिए अलग संकलन समस्या मुझे लगता है कि यह भी एक समस्या है जो मॉड्यूल के प्रवास के साथ उत्पन्न हो रही है, जो वर्तमान में काम कर रही है।


15

इसका मतलब है कि टेम्पलेट कक्षाओं की विधि कार्यान्वयन को परिभाषित करने का सबसे पोर्टेबल तरीका उन्हें टेम्पलेट वर्ग परिभाषा के अंदर परिभाषित करना है।

template < typename ... >
class MyClass
{

    int myMethod()
    {
       // Not just declaration. Add method implementation here
    }
};

15

भले ही ऊपर बहुत सारी अच्छी व्याख्याएँ हैं, फिर भी मैं हेडर और बॉडी में टेम्प्लेट को अलग करने का एक व्यावहारिक तरीका याद कर रहा हूँ।
जब मैं इसकी परिभाषा बदलता हूं, तो मेरी मुख्य चिंता सभी टेम्पलेट उपयोगकर्ताओं के पुनर्मूल्यांकन से बचना है।
टेम्प्लेट बॉडी में सभी टेम्प्लेट इंस्टेंटिएशन होना मेरे लिए व्यवहार्य समाधान नहीं है, क्योंकि टेम्प्लेट लेखक को शायद यह सब पता न हो कि उसका उपयोग और टेम्प्लेट उपयोगकर्ता के पास इसे संशोधित करने का अधिकार नहीं है।
मैंने निम्नलिखित दृष्टिकोण लिया, जो पुराने संकलक के लिए भी काम करता है (gcc 4.3.4, aCC A.03.13)।

प्रत्येक टेम्प्लेट के उपयोग के लिए अपनी स्वयं की हेडर फ़ाइल (UML मॉडल से उत्पन्न) में एक टंकण है। इसके शरीर में तात्कालिकता होती है (जो एक पुस्तकालय में समाप्त होती है जो अंत में जुड़ा हुआ है)।
टेम्प्लेट के प्रत्येक उपयोगकर्ता में वह हेडर फ़ाइल शामिल होती है और वह टंकण का उपयोग करता है।

एक योजनाबद्ध उदाहरण:

MyTemplate.h:

#ifndef MyTemplate_h
#define MyTemplate_h 1

template <class T>
class MyTemplate
{
public:
  MyTemplate(const T& rt);
  void dump();
  T t;
};

#endif

MyTemplate.cpp:

#include "MyTemplate.h"
#include <iostream>

template <class T>
MyTemplate<T>::MyTemplate(const T& rt)
: t(rt)
{
}

template <class T>
void MyTemplate<T>::dump()
{
  cerr << t << endl;
}

MyInstantiatedTemplate.h:

#ifndef MyInstantiatedTemplate_h
#define MyInstantiatedTemplate_h 1
#include "MyTemplate.h"

typedef MyTemplate< int > MyInstantiatedTemplate;

#endif

MyInstantiatedTemplate.cpp:

#include "MyTemplate.cpp"

template class MyTemplate< int >;

main.cpp:

#include "MyInstantiatedTemplate.h"

int main()
{
  MyInstantiatedTemplate m(100);
  m.dump();
  return 0;
}

इस तरह से केवल टेम्प्लेट इंस्टेंटिएशन को पुन: संकलित करने की आवश्यकता होगी, सभी टेम्प्लेट उपयोगकर्ताओं (और निर्भरता) पर नहीं।


1
मुझे MyInstantiatedTemplate.hफ़ाइल और अतिरिक्त MyInstantiatedTemplateप्रकार के अपवाद के साथ यह दृष्टिकोण पसंद है । यदि आप इसका उपयोग नहीं करते हैं, तो यह एक छोटा क्लीनर है। इसे दिखाने वाले एक अलग प्रश्न पर मेरे उत्तर की जाँच करें: stackoverflow.com/a/41292751/4612476
कैमरन टैकलिंड

यह दो दुनियाओं में सर्वश्रेष्ठ है। काश यह उत्तर उच्च श्रेणी का होता! इसके अलावा एक ही विचार के थोड़ा क्लीनर कार्यान्वयन के लिए ऊपर दिए गए लिंक को देखें।
वॉर्मर

8

बस यहाँ कुछ उल्लेखनीय जोड़ने के लिए। जब वे फ़ंक्शन टेम्प्लेट नहीं होते हैं तो क्रियान्वयन फ़ाइल में ठीक एक टेम्प्लेटेड क्लास के तरीकों को परिभाषित कर सकते हैं।


myQueue.hpp:

template <class T> 
class QueueA {
    int size;
    ...
public:
    template <class T> T dequeue() {
       // implementation here
    }

    bool isEmpty();

    ...
}    

myQueue.cpp:

// implementation of regular methods goes like this:
template <class T> bool QueueA<T>::isEmpty() {
    return this->size == 0;
}


main()
{
    QueueA<char> Q;

    ...
}

2
असली आदमी के लिए ??? अगर यह सच है, तो आपके उत्तर को सही के रूप में जांचा जाना चाहिए। किसी को भी उन सभी हैक किए गए वूडू सामानों की आवश्यकता है यदि आप सिर्फ गैर टेम्पलेट सदस्य विधियों को परिभाषित कर सकते हैं।
माइकल IV

खैर यह काम नहीं करता है। कम से कम MSVC 2019 पर, टेम्पलेट वर्ग के सदस्य फ़ंक्शन के लिए अनसुलझे बाहरी प्रतीक प्राप्त करना।
माइकल IV

मेरे पास परीक्षण करने के लिए MSVC 2019 नहीं है। यह C ++ मानक द्वारा अनुमत है। अब, MSVC हमेशा नियमों का पालन नहीं करने के लिए कुख्यात है। यदि आप पहले से नहीं हैं, तो प्रोजेक्ट सेटिंग्स -> C / C ++ -> भाषा -> अनुरूपता मोड -> हाँ (अनुमेय-) आज़माएँ।
निकोस

1
यह सटीक उदाहरण काम करता है लेकिन फिर आप isEmptyइसके अलावा किसी अन्य अनुवाद इकाई से कॉल नहीं कर सकते myQueue.cpp...
MM

7

यदि चिंता अतिरिक्त संकलन समय और द्विआधारी आकार के ब्लोट का उपयोग करके निर्मित है। इसे सभी .cpp मॉड्यूल के भाग के रूप में संकलित करके उपयोग किया जाता है, तो कई मामलों में आप जो कर सकते हैं वह टेम्पलेट वर्ग को गैर-टेम्प्लेटाइज्ड बेस क्लास से नीचे कर देता है। इंटरफ़ेस का गैर-प्रकार-निर्भर भाग, और वह बेस क्लास .cpp फ़ाइल में इसका कार्यान्वयन हो सकता है।


2
इस प्रतिक्रिया को बहुत अधिक प्रतिरूपित किया जाना चाहिए। मैंने " स्वतंत्र रूप से " अपने समान दृष्टिकोण की खोज की और विशेष रूप से किसी और की तलाश कर रहा था, क्योंकि मैंने इसे पहले से ही उपयोग किया है, अगर यह एक आधिकारिक पैटर्न है और क्या इसे नाम मिला है। मेरा दृष्टिकोण यह है कि class XBaseजहां कहीं भी मुझे लागू करने की आवश्यकता है template class X, वहां टाइप-डिपेंडेंट पार्ट्स Xऔर बाकी सभी को लागू करना है XBase
Fabio A.

6

यह बिल्कुल सही है क्योंकि संकलक को यह जानना होगा कि यह किस प्रकार के आवंटन के लिए है। इसलिए टेम्प्लेट क्लासेस, फ़ंक्शंस, एनम आदि। इसे हेडर फ़ाइल में भी लागू किया जाना चाहिए, अगर इसे सार्वजनिक किया जाए या लाइब्रेरी (स्थिर या गतिशील) का हिस्सा बनाया जाए क्योंकि हेडर फ़ाइलों को c / cpp फ़ाइलों के विपरीत संकलित नहीं किया जाता है जो कर रहे हैं। यदि कंपाइलर को पता नहीं है तो टाइप नहीं कर सकता है। .Net में यह इसलिए हो सकता है क्योंकि सभी ऑब्जेक्ट ऑब्जेक्ट क्लास से निकलते हैं। यह .Net नहीं है।


5
"हेडर फाइलें संकलित नहीं की जाती हैं" - यह वर्णन करने का एक बहुत ही अजीब तरीका है। हेडर फाइलें "सी / सीपीपी" फाइल की तरह ही ट्रांसलेशन यूनिट का हिस्सा हो सकती हैं।
फ्लेक्सो

2
वास्तव में, यह लगभग सच्चाई के विपरीत है, जो कि हैडर फाइलें कई बार बहुत बार संकलित की जाती हैं, जबकि एक स्रोत फ़ाइल आमतौर पर एक बार संकलित की जाती है।
xxxon

6

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

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

इसलिए टेम्प्लेट को कभी भी अलग-अलग संकलित नहीं किया जाता है और केवल संकलित किया जाता है, जहां आपके पास किसी अन्य स्रोत फ़ाइल में एक ठोस तात्कालिकता है। हालाँकि, ठोस तात्कालिकता को टेम्प्लेट फ़ाइल के कार्यान्वयन को जानना आवश्यक है, क्योंकि केवल संशोधित करनाtypename T.h फ़ाइल में एक ठोस प्रकार का उपयोग करने से काम नहीं चलने वाला है क्योंकि .cpp को लिंक करने के लिए क्या है, मैं इसे बाद में नहीं ढूंढ सकता क्योंकि याद रखना सार सार है और इसे संकलित नहीं किया जा सकता है, इसलिए मैं मजबूर हूं। कार्यान्वयन अभी देने के लिए इसलिए मुझे पता है कि संकलन और लिंक क्या करना है, और अब मेरे पास कार्यान्वयन है जो इसे एन्क्लेव गाइड फ़ाइल में लिंक हो जाता है। असल में, जिस क्षण मैं एक टेम्पलेट को त्वरित करता हूं, मुझे एक पूरी नई कक्षा बनाने की आवश्यकता होती है, और मैं ऐसा नहीं कर सकता कि अगर मुझे नहीं पता कि उस प्रकार का उपयोग करते समय मुझे उस कक्षा को कैसे देखना चाहिए, जब तक मैं संकलक को नोटिस नहीं करता हूं टेम्पलेट कार्यान्वयन, इसलिए अब कंपाइलर Tमेरे प्रकार के साथ बदल सकता है और एक ठोस वर्ग बना सकता है जो संकलित और लिंक किए जाने के लिए तैयार है।

योग करने के लिए, टेम्पलेट ब्लूप्रिंट हैं कि कक्षाओं को कैसे देखना चाहिए, किसी वस्तु को कैसे देखना चाहिए, इसके लिए कक्षाएं ब्लूप्रिंट हैं। मैं अपने ठोस तात्कालिकता से अलग टेम्पलेट्स संकलित नहीं कर सकता क्योंकि संकलक केवल ठोस प्रकारों को संकलित करता है, दूसरे शब्दों में, कम से कम C ++ में टेम्पलेट्स, शुद्ध भाषा अमूर्त है। हमें बोलने के लिए टेम्पलेट्स को डी-एब्सट्रेक्ट करना है, और हम ऐसा करने के लिए उन्हें एक ठोस प्रकार देते हैं ताकि हमारे टेम्पलेट अमूर्त एक नियमित क्लास फाइल में बदल सकें और बदले में, इसे सामान्य रूप से संकलित किया जा सके। टेम्पलेट .h फ़ाइल और टेम्पलेट .cpp फ़ाइल को अलग करना अर्थहीन है। यह निरर्थक है क्योंकि .cpp और .h का पृथक्करण केवल वही है जहाँ .cpp को अलग-अलग संकलित किया जा सकता है और व्यक्तिगत रूप से लिंक किया जा सकता है, क्योंकि हम उन्हें अलग से संकलित नहीं कर सकते हैं, क्योंकि टेम्पलेट एक अमूर्त हैं,

मतलब typename Tसंकलन के चरण के दौरान लिंक करने का चरण नहीं है, इसलिए यदि मैं Tएक ठोस मूल्य प्रकार के रूप में प्रतिस्थापित किए बिना किसी टेम्पलेट को संकलित करने का प्रयास करता हूं, जो संकलक के लिए पूरी तरह से अर्थहीन है और परिणामस्वरूप ऑब्जेक्ट कोड नहीं बनाया जा सकता है क्योंकि यह नहीं करता है जानिए क्या Tहै।

यह तकनीकी रूप से कुछ प्रकार की कार्यक्षमता बनाने के लिए संभव है जो टेम्पलेट को बचाएगा। सीपीपी फ़ाइल को टाइप करें और जब वे अन्य स्रोतों में मिलते हैं तो उन्हें स्विच आउट करें, मुझे लगता है कि मानक में एक कीवर्ड होता है exportजो आपको एक अलग तरीके से टेम्पलेट डालने की अनुमति देगा। cpp फ़ाइल लेकिन ऐसा नहीं है कि कई संकलक वास्तव में इसे लागू करते हैं।

बस एक साइड नोट, जब टेम्प्लेट क्लास के लिए विशेषज्ञता बनाते हैं, तो आप हेडर को कार्यान्वयन से अलग कर सकते हैं क्योंकि परिभाषा के अनुसार विशेषज्ञता का मतलब है कि मैं एक ठोस प्रकार के लिए विशेषज्ञता प्राप्त कर रहा हूं जिसे व्यक्तिगत रूप से संकलित और लिंक किया जा सकता है।


4

अलग क्रियान्वयन का एक तरीका इस प्रकार है।

//inner_foo.h

template <typename T>
struct Foo
{
    void doSomething(T param);
};


//foo.tpp
#include "inner_foo.h"
template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}


//foo.h
#include <foo.tpp>

//main.cpp
#include <foo.h>

inner_foo के पास आगे की घोषणाएं हैं। foo.tpp का कार्यान्वयन है और इसमें इनर_फू.ह शामिल है; और foo.h में foo.tpp को शामिल करने के लिए सिर्फ एक लाइन होगी।

संकलन समय पर, foo.h की सामग्री को foo.tpp पर कॉपी किया जाता है और फिर पूरी फ़ाइल को foo.h पर कॉपी किया जाता है, जिसके बाद यह संकलन करता है। इस तरह, कोई सीमा नहीं है, और नामकरण एक अतिरिक्त फ़ाइल के बदले में सुसंगत है।

मैं ऐसा इसलिए करता हूं क्योंकि जब कोड * .tpp में कक्षा की आगे की घोषणाओं को नहीं देखता है तो कोड विश्लेषण के लिए स्थैतिक विश्लेषण करता है। किसी भी IDE में कोड लिखने या YouCompleteMe या अन्य का उपयोग करने पर यह कष्टप्रद है।


2
s / inner_foo / foo / g और foo.th को foo.h के अंत में शामिल करें। एक कम फ़ाइल।

1

मेरा सुझाव है कि इस gcc पृष्ठ को देखें जो टेम्पलेट तात्कालिकता के लिए "cfront" और "borland" मॉडल के बीच ट्रेडऑफ़्स पर चर्चा करता है।

https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Template-Instantiation.html

"बोरलैंड" मॉडल लेखक के सुझाव के अनुरूप है, पूर्ण टेम्पलेट की परिभाषा प्रदान करता है, और कई बार संकलित चीजें होती हैं।

इसमें मैनुअल और स्वचालित टेम्पलेट इंस्टेंटेशन का उपयोग करने से संबंधित स्पष्ट सिफारिशें शामिल हैं। उदाहरण के लिए, "-repo" विकल्प का उपयोग उन टेम्पलेट्स को इकट्ठा करने के लिए किया जा सकता है जिन्हें तत्काल करने की आवश्यकता होती है। या एक अन्य विकल्प मैन्युअल टेम्पलेट तात्कालिकता के लिए मजबूर करने के लिए "-fno-implicit-टेम्पलेट्स" का उपयोग करके स्वचालित टेम्पलेट इंस्टेंशन्स को अक्षम करना है।

अपने अनुभव में, मैं प्रत्येक संकलन इकाई (एक टेम्पलेट पुस्तकालय का उपयोग करके) के लिए C ++ मानक पुस्तकालय और बूस्ट टेम्पलेट्स पर निर्भर किया जा रहा है। मेरे बड़े टेम्प्लेट वर्गों के लिए, मैं मैन्युअल टेम्पलेट इंस्टेंटेशन करता हूं, एक बार, मुझे जिन प्रकारों की आवश्यकता होती है।

यह मेरा दृष्टिकोण है क्योंकि मैं एक कार्यशील कार्यक्रम प्रदान कर रहा हूं, अन्य कार्यक्रमों में उपयोग के लिए टेम्पलेट लाइब्रेरी नहीं। पुस्तक के लेखक, जोसुटिस, टेम्पलेट पुस्तकालयों पर बहुत काम करते हैं।

अगर मैं वास्तव में गति के बारे में चिंतित था, तो मुझे लगता है कि मैं Precompiled Headers https://gcc.gnu.org/oniltocs/gcc/Precompiled-Headers.html का उपयोग करके पता लगाऊंगा

जो कई कंपाइलरों में समर्थन प्राप्त कर रहा है। हालाँकि, मुझे लगता है कि पहले से तैयार हेडर टेम्प्लेट हेडर फाइलों के साथ कठिन होंगे।


-2

एक और कारण यह है कि हेडर फ़ाइलों में घोषणाओं और परिभाषाओं दोनों को पढ़ना एक अच्छा विचार है। मान लीजिए कि यूटिलिटी में ऐसा टेम्प्लेट फंक्शन है।

template <class T>
T min(T const& one, T const& theOther);

और Utility.cpp में:

#include "Utility.h"
template <class T>
T min(T const& one, T const& other)
{
    return one < other ? one : other;
}

ऑपरेटर (<) से कम को लागू करने के लिए यहां हर टी क्लास की आवश्यकता होती है। जब आप दो क्लास इंस्टेंस की तुलना करते हैं तो यह कंपाइलर एरर फेंक देगा जिसने "<" को लागू नहीं किया है।

इसलिए यदि आप टेम्प्लेट घोषणा और परिभाषा को अलग करते हैं, तो आप इस एपीआई को अपनी कक्षाओं में इस एपीआई का उपयोग करने के लिए केवल हेडर फ़ाइल को पढ़ने और पढ़ने में सक्षम नहीं होंगे, हालांकि कंपाइलर आपको इसमें बताएगा जिसके बारे में ऑपरेटर को ओवरराइड करने की आवश्यकता होती है।


-7

आप वास्तव में .cpp फ़ाइल के बजाय .template फ़ाइल के अंदर अपने टेम्पलेट वर्ग को परिभाषित कर सकते हैं। जो कोई भी कह रहा है कि आप इसे केवल हेडर फ़ाइल के अंदर परिभाषित कर सकते हैं गलत है। यह कुछ ऐसा है जो सभी तरह से वापस c ++ 98 पर काम करता है।

अपने कंपाइलर को अपने .template फ़ाइल को c ++ फ़ाइल के रूप में समझाना न भूलें कि इंटेलीजेंस समझ रखें।

गतिशील सरणी वर्ग के लिए इसका एक उदाहरण यहां दिया गया है।

#ifndef dynarray_h
#define dynarray_h

#include <iostream>

template <class T>
class DynArray{
    int capacity_;
    int size_;
    T* data;
public:
    explicit DynArray(int size = 0, int capacity=2);
    DynArray(const DynArray& d1);
    ~DynArray();
    T& operator[]( const int index);
    void operator=(const DynArray<T>& d1);
    int size();

    int capacity();
    void clear();

    void push_back(int n);

    void pop_back();
    T& at(const int n);
    T& back();
    T& front();
};

#include "dynarray.template" // this is how you get the header file

#endif

अब आपके अंदर .tplate फ़ाइल आप अपने कार्यों को परिभाषित करते हैं कि आप सामान्य रूप से कैसे करेंगे।

template <class T>
DynArray<T>::DynArray(int size, int capacity){
    if (capacity >= size){
        this->size_ = size;
        this->capacity_ = capacity;
        data = new T[capacity];
    }
    //    for (int i = 0; i < size; ++i) {
    //        data[i] = 0;
    //    }
}

template <class T>
DynArray<T>::DynArray(const DynArray& d1){
    //clear();
    //delete [] data;
    std::cout << "copy" << std::endl;
    this->size_ = d1.size_;
    this->capacity_ = d1.capacity_;
    data = new T[capacity()];
    for(int i = 0; i < size(); ++i){
        data[i] = d1.data[i];
    }
}

template <class T>
DynArray<T>::~DynArray(){
    delete [] data;
}

template <class T>
T& DynArray<T>::operator[]( const int index){
    return at(index);
}

template <class T>
void DynArray<T>::operator=(const DynArray<T>& d1){
    if (this->size() > 0) {
        clear();
    }
    std::cout << "assign" << std::endl;
    this->size_ = d1.size_;
    this->capacity_ = d1.capacity_;
    data = new T[capacity()];
    for(int i = 0; i < size(); ++i){
        data[i] = d1.data[i];
    }

    //delete [] d1.data;
}

template <class T>
int DynArray<T>::size(){
    return size_;
}

template <class T>
int DynArray<T>::capacity(){
    return capacity_;
}

template <class T>
void DynArray<T>::clear(){
    for( int i = 0; i < size(); ++i){
        data[i] = 0;
    }
    size_ = 0;
    capacity_ = 2;
}

template <class T>
void DynArray<T>::push_back(int n){
    if (size() >= capacity()) {
        std::cout << "grow" << std::endl;
        //redo the array
        T* copy = new T[capacity_ + 40];
        for (int i = 0; i < size(); ++i) {
            copy[i] = data[i];
        }

        delete [] data;
        data = new T[ capacity_ * 2];
        for (int i = 0; i < capacity() * 2; ++i) {
            data[i] = copy[i];
        }
        delete [] copy;
        capacity_ *= 2;
    }
    data[size()] = n;
    ++size_;
}

template <class T>
void DynArray<T>::pop_back(){
    data[size()-1] = 0;
    --size_;
}

template <class T>
T& DynArray<T>::at(const int n){
    if (n >= size()) {
        throw std::runtime_error("invalid index");
    }
    return data[n];
}

template <class T>
T& DynArray<T>::back(){
    if (size() == 0) {
        throw std::runtime_error("vector is empty");
    }
    return data[size()-1];
}

template <class T>
T& DynArray<T>::front(){
    if (size() == 0) {
        throw std::runtime_error("vector is empty");
    }
    return data[0];
    }

2
ज्यादातर लोग हेडर फ़ाइल को कुछ भी परिभाषित करते हैं जो कि स्रोत फ़ाइलों के लिए परिभाषाओं का प्रचार करता है। तो आपने फ़ाइल एक्सटेंशन ".template" का उपयोग करने का निर्णय लिया होगा, लेकिन आपने एक हेडर फ़ाइल लिखी है।
टॉमी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.