टेम्पर्ड C ++ कक्षाओं को .hpp / .cpp फ़ाइलों में विभाजित करना - क्या यह संभव है?


96

मुझे C ++ टेम्पलेट क्लास को संकलित करने की कोशिश करने में त्रुटि हो रही है जो कि एक .hppऔर .cppफ़ाइल के बीच विभाजित है :

$ g++ -c -o main.o main.cpp  
$ g++ -c -o stack.o stack.cpp   
$ g++ -o main main.o stack.o  
main.o: In function `main':  
main.cpp:(.text+0xe): undefined reference to 'stack<int>::stack()'  
main.cpp:(.text+0x1c): undefined reference to 'stack<int>::~stack()'  
collect2: ld returned 1 exit status  
make: *** [program] Error 1  

यहाँ मेरा कोड है:

स्टैक.हप्प :

#ifndef _STACK_HPP
#define _STACK_HPP

template <typename Type>
class stack {
    public:
            stack();
            ~stack();
};
#endif

stack.cpp :

#include <iostream>
#include "stack.hpp"

template <typename Type> stack<Type>::stack() {
        std::cerr << "Hello, stack " << this << "!" << std::endl;
}

template <typename Type> stack<Type>::~stack() {
        std::cerr << "Goodbye, stack " << this << "." << std::endl;
}

main.cpp :

#include "stack.hpp"

int main() {
    stack<int> s;

    return 0;
}

ldबिल्कुल सही है: प्रतीक अंदर नहीं हैं stack.o

इस प्रश्न का उत्तर मदद नहीं करता है, जैसा कि मैं पहले ही कह रहा हूं कि यह कह रहा है।
यह एक मदद कर सकता है, लेकिन मैं .hppफ़ाइल में हर एक विधि को स्थानांतरित नहीं करना चाहता- मुझे नहीं करना चाहिए, क्या मुझे करना चाहिए?

.cppफ़ाइल में फ़ाइल में सब कुछ स्थानांतरित करने के लिए एकमात्र उचित समाधान है .hpp, और बस स्टैंडअलोन ऑब्जेक्ट फ़ाइल के रूप में लिंक के बजाय सब कुछ शामिल है? यह भयानक बदसूरत लगता है ! उस मामले में, मैं भी अपने पिछले राज्य और नाम बदलने पर वापस लौटने सकता stack.cppकरने के लिए stack.hppऔर इसके साथ किया जाए।


जब आप वास्तव में अपने कोड को (बाइनरी फ़ाइल में) छिपा कर रखना चाहते हैं या उसे साफ़ रखना चाहते हैं, तो इसके लिए दो बेहतरीन वर्कअराउंड हैं। सामान्य स्थिति को कम करने की आवश्यकता है, हालांकि पहली स्थिति में। इसे यहाँ समझाया गया है: stackoverflow.com/questions/495021/…
शारिक

जवाबों:


151

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

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

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

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

मुझे उम्मीद है कि यह चर्चा मददगार होगी।


2
"यह समझना चाहिए कि टेम्पलेट वर्ग एक वर्ग नहीं है" - क्या यह दूसरा तरीका नहीं था? क्लास टेम्पलेट एक टेम्पलेट है। "टेम्प्लेट क्लास" का उपयोग कभी-कभी "टेम्प्लेट की तात्कालिकता" के स्थान पर किया जाता है, और एक वास्तविक वर्ग होगा।
Xupicor

सिर्फ संदर्भ के लिए, यह कहना सही नहीं है कि कोई वर्कअराउंड नहीं है! डेटा स्ट्रक्चर्स को तरीकों से अलग करना भी एक बुरा विचार है क्योंकि यह एनकैप्सुलेशन द्वारा विरोध किया जाता है। एक बढ़िया वर्कअराउंड है जिसे आप कुछ स्थितियों में उपयोग कर सकते हैं (मुझे सबसे अधिक विश्वास है): stackoverflow.com/questions/495021/…
शायर

@Xupicor, आप सही हैं। तकनीकी रूप से "क्लास टेम्प्लेट" वह है जो आप लिखते हैं ताकि आप एक "टेम्प्लेट क्लास" और इसके संबंधित ऑब्जेक्ट को तुरंत लिख सकें। हालांकि, मेरा मानना ​​है कि एक सामान्य शब्दावली में, दोनों शब्दों का परस्पर विनिमय से उपयोग करना गलत नहीं होगा, "क्लास टेम्प्लेट" को परिभाषित करने के लिए वाक्यविन्यास शब्द "टेम्पलेट" से शुरू होता है, न कि "क्लास" से।
शारजीथ एन।

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

इस काम को करने के लिए मुझे जो निकटतम चीज़ मिली, वह है .h / .hpp फ़ाइलों की एक जोड़ी का उपयोग करना, और # टेम्पलेट फ़ाइल को परिभाषित करने वाली .h फ़ाइल के अंत में #include "filename.hpp"। (अर्धविराम के साथ वर्ग परिभाषा के लिए अपने समापन ब्रेस के नीचे)। यह कम से कम संरचनात्मक रूप से उन्हें फिल्म बनाने से अलग करता है, और अनुमति दी जाती है क्योंकि अंत में, कंपाइलर आपके #include "filename.hpp" पर अपने .hpp कोड को कॉपी / पेस्ट करता है।
आर्टोरियस 2718

90

यह है लंबे समय के रूप के रूप में आप जानते हैं कि instantiations आप की जरूरत करने जा रहे हैं संभव है,।

Stack.cpp के अंत में निम्नलिखित कोड जोड़ें और यह काम करेगा:

template class stack<int>;

स्टैक के सभी गैर-टेम्पलेट तरीके त्वरित होंगे, और लिंकिंग चरण ठीक काम करेगा।


7
व्यवहार में, ज्यादातर लोग इसके लिए एक अलग सीपीपी फाइल का उपयोग करते हैं - स्टैकस्टैंटिएशन.सीपी जैसी कुछ।
नेमेनजा ट्रिफ़ुनोविक

@NemanjaTrifunovic आप इस बात का उदाहरण दे सकते हैं कि स्टैकस्टैन्टिएशन.कैप कैसा दिखेगा?
qwerty9967

3
वास्तव में अन्य समाधान हैं: codeproject.com/Articles/48575/…
स्लीपसॉर्ट

@ बेनोइट से मुझे एक त्रुटि मिली: उम्मीद से पहले अयोग्य-आईडी ';' टोकन टेम्पलेट स्टैक <int>; तुम जानते हो क्यों? धन्यवाद!
कैमिनो

3
दरअसल, सही सिंटैक्स है template class stack<int>;
पॉल बाल्टस्क्यू

8

आप इसे इस तरह से कर सकते हैं

// xyz.h
#ifndef _XYZ_
#define _XYZ_

template <typename XYZTYPE>
class XYZ {
  //Class members declaration
};

#include "xyz.cpp"
#endif

//xyz.cpp
#ifdef _XYZ_
//Class definition goes here

#endif

इस पर डान्वेब में चर्चा की गई है

एफएक्यू में भी लेकिन C ++ एक्सपोर्ट कीवर्ड का उपयोग करके।


5
includeआईएनजी cppफ़ाइल आम तौर पर एक भयानक विचार है। यहां तक ​​कि अगर आपके पास इसके लिए एक वैध कारण है, तो फ़ाइल - जो वास्तव में सिर्फ एक महिमामंडित शीर्षलेख है - को बहुत स्पष्ट करने के लिए एक hppया कुछ अलग एक्सटेंशन (जैसे tpp) दिए जाने चाहिए कि क्या चल रहा है, वास्तविक फ़ाइलों को makefileलक्षित करने के आसपास भ्रम को दूर करें, आदि। cpp
अंडरस्कोर_ड

@underscore_d क्या आप बता सकते हैं कि .cppफ़ाइल सहित एक भयानक विचार क्यों है?
अब्बास

1
@ अब्बास क्योंकि एक्सटेंशन cpp(या cc, या cजो भी) इंगित करता है कि फ़ाइल कार्यान्वयन का एक टुकड़ा है, जिसके परिणामस्वरूप अनुवाद इकाई (प्रीप्रोसेसर आउटपुट) अलग से संकलित है, और यह कि फ़ाइल की सामग्री केवल एक बार संकलित की जाती है। यह इंगित नहीं करता है कि फ़ाइल इंटरफ़ेस का पुन: प्रयोज्य हिस्सा है, जिसे कहीं भी मनमाने ढंग से शामिल किया जाना है। #includeएक वास्तविक cpp फ़ाइल आईएनजी जल्दी से कई परिभाषा त्रुटियों के साथ आपकी स्क्रीन को भर देगी, और ठीक ही। इस मामले में, जैसा कि इसके लिए एक कारण है #include, cppसिर्फ विस्तार का गलत विकल्प था।
अंडरस्कोर_ड

@underscore_d तो मूल रूप से .cppइस तरह के उपयोग के लिए एक्सटेंशन का उपयोग करना गलत है । लेकिन एक और कहावत .tppका उपयोग करना पूरी तरह से ठीक है, जो एक ही उद्देश्य की पूर्ति करेगा लेकिन आसान / त्वरित समझ के लिए एक अलग एक्सटेंशन का उपयोग करेगा?
अब्बास

1
@ अब्बास हां, cpp/ cc/ आदि से बचा जाना चाहिए, लेकिन इसके अलावा कुछ का उपयोग करने के लिए एक अच्छा विचार है hpp- उदाहरण tppके लिए tcc, आदि - इसलिए आप बाकी फ़ाइल नाम का पुन: उपयोग कर सकते हैं और इंगित कर सकते हैं कि tppफ़ाइल, हालांकि यह एक हेडर की तरह काम करती है, संबंधित में टेम्पलेट घोषणाओं के आउट-ऑफ-लाइन कार्यान्वयन को धारण करता है hpp। तो यह पोस्ट एक अच्छे आधार के साथ शुरू होती है - घोषणाओं और परिभाषाओं को 2 अलग-अलग फाइलों में अलग करना, जो कि ग्रैक / grep के लिए आसान हो सकती है या कभी-कभी परिपत्र निर्भरता IME के ​​कारण आवश्यक होती है - लेकिन फिर यह सुझाव देकर बुरी तरह से समाप्त हो जाती है कि दूसरी फ़ाइल का गलत एक्सटेंशन है
अंडरस्कोर_ड

6

नहीं, यह संभव नहीं है। exportकीवर्ड के बिना नहीं , जो सभी इरादों और उद्देश्यों के लिए वास्तव में मौजूद नहीं है।

आप जो सबसे अच्छा काम कर सकते हैं, वह अपने फ़ंक्शन कार्यान्वयन को ".tcc" या ".tpp" फ़ाइल में, और .hcc फ़ाइल के अंत में .tcc फ़ाइल में रखें। हालाँकि यह महज कॉस्मेटिक है; यह अभी भी हेडर फ़ाइलों में सब कुछ लागू करने के समान है। यह केवल वह मूल्य है जो आप टेम्प्लेट का उपयोग करने के लिए भुगतान करते हैं।


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

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

3

मेरा मानना ​​है कि टेम्पर्ड कोड को हेडर और कैप में अलग करने की कोशिश करने के दो मुख्य कारण हैं:

एक मात्र लालित्य के लिए है। हम सभी को कोड लिखना पसंद है जो पढ़ने, प्रबंधित करने और बाद में पुन: उपयोग करने योग्य है।

अन्य संकलन समय की कमी है।

मैं वर्तमान में (हमेशा की तरह) OpenCL के साथ संयोजन के रूप में सिमुलेशन सॉफ्टवेयर कोडिंग कर रहा हूं और हमें कोड रखना पसंद है इसलिए इसे HW क्षमता के आधार पर फ्लोट (cl_float) या डबल (cl_double) प्रकारों का उपयोग करके चलाया जा सकता है। अभी यह कोड की शुरुआत में एक #define वास्तविक का उपयोग करके किया जाता है, लेकिन यह बहुत सुरुचिपूर्ण नहीं है। वांछित सटीकता को बदलने के लिए आवेदन को फिर से जमा करने की आवश्यकता होती है। चूंकि वास्तविक रन-टाइम प्रकार नहीं हैं, इसलिए हमें इस समय के साथ रहना होगा। सौभाग्य से ओपनसीएल गुठली को रनटाइम संकलित किया जाता है, और एक सरल आकार (REAL) हमें तदनुसार कर्नेल कोड परिवर्तन को बदलने की अनुमति देता है।

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


2

अगर आप #include "stack.cppके अंत में केवल stack.hpp। यदि कार्यान्वयन अपेक्षाकृत बड़ा है, तो मैं केवल इस दृष्टिकोण की सिफारिश करूंगा और यदि आप .cpp फ़ाइल को किसी अन्य एक्सटेंशन में बदल देते हैं, तो इसे नियमित कोड से अलग करने के लिए।


4
यदि आप ऐसा कर रहे हैं, तो आप अपनी stack.cpp फ़ाइल में #ifndef STACK_CPP (और मित्र) जोड़ना चाहेंगे।
स्टीफन नेवेल

मुझे इस सुझाव पर हराया। मैं भी शैली कारणों से इस दृष्टिकोण को पसंद नहीं करता।
ल्यूक

2
हां, ऐसे मामले में, दूसरी फ़ाइल को निश्चित रूप से एक्सटेंशन cpp(या ccया जो भी) नहीं दिया जाना चाहिए क्योंकि यह इसकी वास्तविक भूमिका के विपरीत है। इसके बजाय इसे एक अलग एक्सटेंशन दिया जाना चाहिए जो इंगित करता है कि यह (ए) हेडर है और (बी) एक हेडर को दूसरे हेडर के नीचे शामिल किया जाना है । मैं का उपयोग tppइस, जो handily भी के लिए खड़े हो सकते हैं के लिए tउन्हें pदेर im plementation (आउट-ऑफ-लाइन परिभाषाओं)। मैंने इसके बारे में यहां और अधिक जानकारी दी: stackoverflow.com/questions/1724036/…
underscore_d

2

कभी-कभी cpp फ़ाइल में अधिकांश कार्यान्वयन को छिपाना संभव होता है, यदि आप सामान्य टेम्पलेट foo को सभी टेम्पलेट मापदंडों को गैर-टेम्पलेट वर्ग (संभवतः प्रकार-असुरक्षित) में निकाल सकते हैं। फिर हेडर में उस कक्षा में पुनर्निर्देशन कॉल शामिल होंगे। "टेम्पलेट ब्लोट" समस्या से लड़ते समय इसी तरह के दृष्टिकोण का उपयोग किया जाता है।


+1 - भले ही यह ज्यादातर समय बहुत अच्छी तरह से काम नहीं करता है (कम से कम, जितनी बार मैं चाहता हूं)
पीटरचेन

2

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

DLLs (!) पर इनका निर्यात करना भी संभव है, लेकिन सिंटेक्स अधिकार (__declspec (dllexport) और निर्यात कीवर्ड) का एमएस-विशिष्ट संयोजन प्राप्त करने के लिए यह बहुत मुश्किल है।

हमने उस गणित / जियोम लिब में उपयोग किया है जो कि डबल / फ्लोट को टेम्पलेट करता है, लेकिन इसमें काफी कोड था। (मैं उस समय इसके लिए चारों ओर से गुहार लगा चुका था, हालांकि आज वह कोड नहीं है।)


2

समस्या यह है कि एक टेम्पलेट वास्तविक वर्ग उत्पन्न नहीं करता है, यह सिर्फ एक टेम्पलेट है जो संकलक को यह बताता है कि कक्षा कैसे उत्पन्न की जाए। आपको एक ठोस वर्ग उत्पन्न करने की आवश्यकता है।

हेडर फ़ाइल में विधियों को डालना आसान और प्राकृतिक तरीका है। लेकिन एक और तरीका है।

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

नई stack.cpp:

#include <iostream>
#include "stack.hpp"
template <typename Type> stack<Type>::stack() {
        std::cerr << "Hello, stack " << this << "!" << std::endl;
}
template <typename Type> stack<Type>::~stack() {
        std::cerr << "Goodbye, stack " << this << "." << std::endl;
}
static void DummyFunc() {
    static stack<int> stack_int;  // generates the constructor and destructor code
    // ... any other method invocations need to go here to produce the method code
}

8
आपको डमी फ़ंक्शन की आवश्यकता नहीं है: 'टेम्पलेट स्टैक <int>;' का उपयोग करें यह वर्तमान संकलन इकाई में टेम्प्लेट के एक इंस्टेंस को बाध्य करता है। बहुत उपयोगी है यदि आप एक टेम्पलेट को परिभाषित करते हैं लेकिन केवल एक साझा देयता में कुछ विशिष्ट कार्यान्वयन चाहते हैं।
मार्टिन यॉर्क

@ सदस्य: सभी सदस्य कार्यों सहित? यह बढ़िया है। आपको इस सुझाव को "छिपी C ++ सुविधाओं" धागे में जोड़ना चाहिए।
मार्क रैनसम

@LokiAstari मुझे इस पर एक लेख मिला जिस के मामले में कोई और सीखना चाहता है: cplusplus.com/forum/articles/14272
एंड्रयू लार्सन

1

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

एक चीज जो मैं करता हूं, वह है कि मैं अपने टेम्प्लेट को जेनेरिक नॉन-टेम्पलेटेड पार्ट में विभाजित करने की कोशिश करता हूं (जिसे cpp / hpp के बीच विभाजित किया जा सकता है) और टाइप-स्पेसिफिक टेम्प्लेट पार्ट जो नॉन-टेम्पलेटेड क्लास को इनहेरिट करता है।


0

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


0

एक और संभावना कुछ करने की है:

#ifndef _STACK_HPP
#define _STACK_HPP

template <typename Type>
class stack {
    public:
            stack();
            ~stack();
};

#include "stack.cpp"  // Note the include.  The inclusion
                      // of stack.h in stack.cpp must be 
                      // removed to avoid a circular include.

#endif

मैं इस सुझाव को शैली के मामले में नापसंद करता हूं, लेकिन यह आपके अनुरूप हो सकता है।


1
शामिल किए जा रहे शानदार 2 हेडर में वास्तविक स्रोत फ़ाइलों के cppसाथ भ्रम से बचने के लिए कम से कम एक एक्सटेंशन होना चाहिए । आम सुझावों में शामिल हैं और । tpptcc
अंडरस्कोर_ड

0

'निर्यात' कीवर्ड टेम्पलेट घोषणा से टेम्पलेट कार्यान्वयन को अलग करने का तरीका है। यह मौजूदा कार्यान्वयन के बिना C ++ मानक में पेश किया गया था। वास्तव में केवल कुछ ही कंपाइलरों ने इसे लागू किया। निर्यात पर सूचित आईटी लेख में गहराई से जानकारी पढ़ें


1
यह लगभग एक लिंक केवल उत्तर है, और यह लिंक मृत है।
अंडरस्कोर_ड

0

1) याद रखें कि .h और .cpp फ़ाइलों को अलग करने का मुख्य कारण एक अलग संकलित ओबज कोड के रूप में वर्ग कार्यान्वयन को छिपाना है जिसे उपयोगकर्ता के कोड से जोड़ा जा सकता है जिसमें एक .h वर्ग शामिल है।

2) गैर-टेम्पलेट कक्षाओं में सभी चर समवर्ती और विशेष रूप से .h और .cpp फ़ाइलों में परिभाषित होते हैं। इसलिए कंपाइलर को ऑब्जेक्ट / मशीन कोड तैयार करने / अनुवाद करने से पहले कक्षा में उपयोग किए जाने वाले सभी डेटा प्रकारों के बारे में जानकारी की आवश्यकता होगी। प्रकार:

        TClass<int> myObj;

3) इस तात्कालिकता के बाद ही, कंपाइल पास किए गए डेटा प्रकार (एस) से मिलान करने के लिए टेम्पलेट वर्ग के विशिष्ट संस्करण को उत्पन्न करता है।

4) इसलिए, .cpp को उपयोगकर्ताओं के विशिष्ट डेटा प्रकार को जाने बिना अलग से संकलित नहीं किया जा सकता है। इसलिए इसे ".h" के भीतर स्रोत कोड के रूप में रहना होगा जब तक कि उपयोगकर्ता आवश्यक डेटा प्रकार निर्दिष्ट नहीं करता है, तब तक इसे एक विशिष्ट डेटा प्रकार के लिए तैयार किया जा सकता है


0

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

कुछ उपयोगी जानकारी यहाँ है: https://docs.microsoft.com/en-us/cpp/cpp/explicit-instantiation?view=vs-2019

अपने उसी उदाहरण के लिए: Stack.hpp

template <class T>
class Stack {

public:
    Stack();
    ~Stack();
    void Push(T val);
    T Pop();
private:
    T val;
};


template class Stack<int>;

stack.cpp

#include <iostream>
#include "Stack.hpp"
using namespace std;

template<class T>
void Stack<T>::Push(T val) {
    cout << "Pushing Value " << endl;
    this->val = val;
}

template<class T>
T Stack<T>::Pop() {
    cout << "Popping Value " << endl;
    return this->val;
}

template <class T> Stack<T>::Stack() {
    cout << "Construct Stack " << this << endl;
}

template <class T> Stack<T>::~Stack() {
    cout << "Destruct Stack " << this << endl;
}

main.cpp

#include <iostream>
using namespace std;

#include "Stack.hpp"

int main() {
    Stack<int> s;
    s.Push(10);
    cout << s.Pop() << endl;
    return 0;
}

आउटपुट:

> Construct Stack 000000AAC012F8B4
> Pushing Value
> Popping Value
> 10
> Destruct Stack 000000AAC012F8B4

मैं इस दृष्टिकोण को पूरी तरह से पसंद नहीं करता, क्योंकि इससे एप्लिकेशन को पैर में खुद को गोली मारने की अनुमति मिलती है, टेम्पलेट क्लास को गलत डेटाटाइप्स पारित करके। उदाहरण के लिए, मुख्य कार्य में, आप अन्य प्रकारों को पारित कर सकते हैं जिन्हें अंतर्निहित रूप से s.Push (1.2) जैसे int में परिवर्तित किया जा सकता है; और यह सिर्फ मेरी राय में बुरा है।


-3

मैं दृश्य स्टूडियो 2010 के साथ काम कर रहा हूं, अगर आप अपनी फ़ाइलों को।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.