2009-10-12 19 views
48

मुझे समझ में नहीं आ रहा है, क्यों हम हेडर में सामान्य (गैर-टेम्पलेट) कक्षा के स्थैतिक चर को परिभाषित करते हैं, हमारे पास लिंकर त्रुटि है, लेकिन टेम्पलेट्स के मामले में सभी ठीक काम करते हैं और इसके अलावा हमारे पास एक ही उदाहरण होगा सभी अनुवाद इकाइयों के बीच स्थिर चर:टेम्पलेट स्थिर चर

यह टेम्पलेट हैडर (template.h) है:

// template.h 
template<typename T> 
class Templ { 
public: 
    static int templStatic; 
}; 

template<typename T> Templ<T>::templStatic = 0; 

यह पहली इकाई टेम्पलेट का उपयोग कर (unit1.cpp)

// unit1.cpp 
#include "template.h" 

int method1() { 
    return Templ<void>::templStatic++; 
} 

दूसरा इकाई है वह फिर से (unit2.cpp):

// unit2.cpp 
#include "template.h" 
int method2() { 
    return Templ<void>::templStatic++; 
} 

और अंत में, main.cpp:

0 
1 
:

// main.cpp 
#include <iostream> 
int method1(); 
int method2(); 

int main(int argc, char** argv) { 
    std::cout << method1() << std::endl; 
    std::cout << method2() << std::endl; 
} 

, compilling जोड़ने और इस कोड को क्रियान्वित करने के बाद, हम उत्पादन निम्नलिखित होगा

तो, क्यों टेम्पलेट्स के मामले में सभी ठीक काम करते हैं (और अपेक्षित के रूप में)? कैसे कंपाइलर या लिंकर इसे संभालते हैं (हम प्रत्येक .cpp फ़ाइल को संकलक के अलग-अलग कॉलिंग में संकलित कर सकते हैं, और फिर उन्हें लिंकर से कैलिंग के साथ लिंक कर सकते हैं, इसलिए कंपाइलर और लिंकर एक ही समय में सभी .cpp फ़ाइलों को "देखें" नहीं देखते हैं)?

पुनश्च: मेरे संकलक: msvcpp 9 (लेकिन MinGW पर भी चेक किए गए)

+0

यदि आप हमें वह कोड दिखाते हैं जो ** ** काम नहीं करता है तो यह अधिक उपयोगी होगा। – JesperE

+0

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

उत्तर

54

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

template<typename T> 
struct F { 
    static int const value; 
}; 

template<typename T> 
int const F<T>::value = sizeof(T); 

निम्नलिखित यह ज्ञात नहीं है क्या T है - मानक कहते वर्ग टेम्पलेट बाहर परिभाषा एक टेम्पलेट परिभाषा है , जिसमें पैरामीटर अपने क्लास टेम्पलेट मालिक से विरासत में प्राप्त होते हैं।


मैंने जीसीसी के साथ कुछ प्रयोग किया है। निम्नलिखित में, हमारे पास F<float>::value का एक निहित तत्कालता है, और F<char>::value का एक स्पष्ट विशेषज्ञता है जिसे एक .cpp फ़ाइल में परिभाषित किया जाना है ताकि कई बार शामिल होने पर डुप्लिकेट प्रतीक त्रुटियों का कारण न हो।

// Translation Unit 1 
template<typename T> 
struct F { 
    static int value; 
}; 

template<typename T> 
int F<T>::value = sizeof(T); 

// this would belong into a .cpp file 
template<> int F<char>::value = 2; 

// this implicitly instantiates F<float>::value 
int test = F<float>::value; 

int main() { } 

दूसरा अनुवाद इकाई में एक ही स्थिर डेटा सदस्य

template<typename T> 
struct F { 
    static int value; 
}; 

template<typename T> 
int F<T>::value = sizeof(T); 

int test1 = F<float>::value; 

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

objdump -Ct main1.o # => 
# cut down to the important ones 
00000000 l df *ABS* 00000000 main1.cpp 
0000000a l  F .text 0000001e __static_initialization_and_destruction_0(int, int) 
00000000 l d .data._ZN1FIfE5valueE 00000000 .data._ZN1FIfE5valueE 
00000028 l  F .text 0000001c global constructors keyed to _ZN1FIcE5valueE 
00000000 g  O .data 00000004 F<char>::value 
00000000 g  O .bss 00000004 test 
00000000 g  F .text 0000000a main 
00000000 w O .data._ZN1FIfE5valueE 00000004 F<float>::value 

हैं तो जैसा कि हम देख सकते हैं F<float>::value एक कमजोर प्रतीक है जिसका अर्थ है लिंकर लिंक समय में इनमें से कई देख सकते हैं ।test, main और F<char>::value वैश्विक (गैर-कमजोर) प्रतीक हैं। main1.o और main2.o एक साथ लिंक करने के लिए, हम नक्शा निर्गम (-Wl,-M) निम्नलिखित

# (mangled name) 
.data._ZN1FIfE5valueE 
    0x080497ac  0x4 main1.o            
    0x080497ac    F<float>::value 

यह बताता है कि वास्तव में यह सब एक उदाहरण को छोड़कर चला जाता है में देखते हैं।

+0

ठीक है। लेकिन कैसे लिंकर, जो दो "टेम्पलेट टेम्पल :: templStatic = 0;" परिभाषाएं (unit1.cpp और unit2.cpp में) इस स्थिति को संभालती है? क्या ऑब्जेक्ट फ़ाइलों में कुछ सी ++ - विशिष्ट मेटा जानकारी है, जो लिंकर से कह सकती है, कि एक परिभाषा को अनदेखा किया जा सकता है (और, नतीजतन हमारे पास "एकाधिक परिभाषाएं" लिंकर त्रुटि नहीं है)? – cybevnm

+0

ने कुछ जीसीसी सामान जोड़े –

1

वहाँ समाधान है, आप एक माता पिता के वर्ग बना सकते हैं और उस में स्थिर चर रख सकते हैं, तो अपने टेम्पलेट वर्ग यह निजी तौर पर वारिस बनाने के लिए, यहाँ एक उदाहरण है:

class Parent 
{ 
protected: 
    static long count; 
}; 

long Parent::count = 0; 

template<typename T> 
class TemplateClass: private Parent 
{ 
private: 
    int mKey; 
public: 
    TemplateClass():mKey(count++){} 
    long getKey(){return mKey;} 
} 

int main() 
{ 
    TemplateClass<int> obj1; 
    TemplateClass<double> obj2; 

    std::cout<<"Object 1 key is: "<<obj1.getKey()<<std::endl; 
    std::cout<<"Object 2 key is: "<<obj2.getKey()<<std::endl; 

    return 0; 
} 

आउटपुट होगा:

Object 1 key is: 0 
Object 2 key is: 1 
संबंधित मुद्दे