2013-05-06 6 views
7

मेरे पास मेरे सी ++ 11 एक्सकोड प्रोजेक्ट में टेम्पलेट किए गए फ़ंक्शन हैं और उनमें से कुछ में विशेषज्ञता है। हालांकि, मैंने पाया है कि विशेषज्ञता केवल डीबग बिल्ड में बुलाया जाता है; अगर मैं रिलीज में निर्माण करता हूं, तो उन्हें नजरअंदाज कर दिया जाता है।मेरा विशेष टेम्पलेट फ़ंक्शन केवल डीबग बिल्ड में क्यों लागू होता है?

मैं सफलतापूर्वक एक बहुत ही सरल उदाहरण बनाया है:

special.h

#include <cstdio> 

struct special 
{ 
    template<typename T> 
    void call(const T&) { puts("not so special"); } 
}; 

special.cpp

#include "special.h" 
#include <string> 

template<> 
void special::call(const std::string&) { puts("very special"); } 

main.cpp

#include "special.h" 
#include <string> 

int main() 
{ 
    std::string str = "hello world"; 
    special s; 
    s.call(123); 
    s.call(str); 
} 

You can download the project (जब तक कहीं 2013 की गर्मियों में कम से कम) समस्या को ठीक करने अगर आप इसे अपने आप को बनाने के लिए नहीं करना चाहती। पहले डीबग कॉन्फ़िगरेशन के साथ प्रोजेक्ट चलाएं, फिर इसे रिलीज़ में फिर से चलाएं। उत्पादन है कि मैं उम्मीद है:

ऐसी क्या खास बात नहीं
बहुत ही खास

और यह वास्तव में है क्या मैं डीबग बिल्ड विन्यास के साथ मिलता है। हालांकि, रिलीज के साथ, मैं इस मिल:

ऐसी क्या खास बात
ऐसी क्या खास बात नहीं

special.cpp में special::call के विशेष कार्यान्वयन जिसका मतलब है नजरअंदाज कर दिया गया नहीं।

परिणाम असंगत क्यों है? यह सुनिश्चित करने के लिए मुझे क्या करना चाहिए कि रिलीज बिल्ड में विशेष कार्य कहा जाता है?

+0

इसे .cpp के बजाय .h में रखें? – Pubby

+0

special.cpp दोनों मामलों में जुड़ा हुआ है? – ForEveR

+0

हां, special.cpp संकलित और दोनों मामलों में जुड़ा हुआ है। अगर मैं हेडर फ़ाइल में विशेषज्ञता डालता हूं, तो यह काम करता है अगर फ़ाइल केवल एक .cpp फ़ाइल में शामिल है, लेकिन अन्यथा मुझे डुप्लिकेट प्रतीक त्रुटि मिलती है। – zneak

उत्तर

11

आपके कार्यक्रम में यूबी है। उपयोग करने से पहले एक स्पष्ट विशेषज्ञता या कम से कम इसकी घोषणा दिखाई देनी चाहिए। [Temp.expl.spec] §6:

एक टेम्पलेट, एक सदस्य टेम्पलेट या एक वर्ग टेम्पलेट का एक सदस्य है, तो स्पष्ट रूप से विशेष तो है कि विशेषज्ञता है कि विशेषज्ञता के पहले उपयोग करने से पहले घोषित किया जाएगा कि होगा में प्रत्येक अनुवाद इकाई में होने वाली निहित तत्कालता का कारण बनता है जो ऐसा उपयोग होता है; कोई निदान की आवश्यकता नहीं है।

को यह घोषणा जोड़ें:

template<> 
void special::call(const std::string&); 

वैकल्पिक रूप से, आप शीर्ष लेख में specialistation ही रख सकते हैं। हालांकि, एक विशेषज्ञता के रूप में अब एक टेम्पलेट नहीं है, यह सामान्य फ़ंक्शन नियमों का पालन करता है और शीर्षलेख में रखे जाने पर inline चिह्नित किया जाना चाहिए।

साथ ही, सावधान रहें कि फ़ंक्शन टेम्पलेट विशेषज्ञता के बजाय विशिष्ट व्यवहार है, और आमतौर पर विशेषज्ञता के मुकाबले ओवरलोड का उपयोग करना बेहतर होता है। विवरण के लिए Herb Sutter's article देखें।

8

आपने एक परिभाषा नियम (ओडीआर) का उल्लंघन किया। तो वास्तव में क्या होता है? main.cpp में special::call<string> के लिए कोई विशेषज्ञता नहीं है। इसलिए संकलक उस अनुवाद इकाई (टीयू) में टेम्पलेट का एक त्वरण उत्पन्न करता है जो "इतना खास नहीं" आउटपुट करता है। special.cpp में एक पूर्ण विशेषज्ञता घोषित और परिभाषित है, इसलिए संकलक उस परिभाषा को अन्य अनुवाद इकाई में डालता है। तो आपके पास दो अलग-अलग अनुवाद इकाइयों में एक ही कार्य की दो अलग-अलग परिभाषाएं हैं, जो ओडीआर का उल्लंघन है जिसका अर्थ है कि यह अपरिभाषित व्यवहार है।

सिद्धांत रूप में, परिणाम कुछ भी हो सकता है। एक कंपाइलर त्रुटि, एक क्रैश, पिज्जा के लिए एक मूक ऑनलाइन ऑर्डर, कुछ भी। डीबग और रिलीज संकलन में भी अलग व्यवहार।

व्यवहार में, मैं लगता निम्न होता है: जब डीबग बिल्ड जोड़ने, लिंकर एक ही प्रतीक दो टुस है, जो केवल टेम्पलेट्स और इनलाइन कार्यों के लिए अनुमति दी है में दो बार परिभाषित देखता है। ओडीआर की वजह से यह माना जा सकता है कि दोनों परिभाषाएं बराबर हैं और special.cpp से एक को चुनती हैं, इसलिए आप संयोग से प्राप्त व्यवहार को प्राप्त करते हैं।
रिलीज बिल्ड के दौरान, संकलक special::call<string> पर main.cpp के संकलन के दौरान कॉल को रेखांकित करता है, इसलिए आपको उस टीयू में एकमात्र व्यवहार मिलता है: "इतना खास नहीं"।

तो आप इसे कैसे ठीक कर सकते हैं?
आदेश में है कि विशेषज्ञता के लिए केवल एक ही परिभाषा है, आप करने के लिए है के रूप में तुमने किया था एक टीयू में परिभाषित है, लेकिन आपके पास घोषित करने के लिए किसी अन्य टीयू में एक पूर्ण विशेषज्ञता है कि वहाँ है, जिसका अर्थ घोषणा करते हैं कि विशेषज्ञता जो अधिक बार देखा जाता है

// in special.h 
template<> 
void special::call(const std::string&); 

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

// in special.h 
template<> 
inline void special::call(const std::string&) 
{ 
    puts("very special"); 
} 
+0

+1 क्या हो रहा है, इसकी पूरी व्याख्या के लिए, और केवल सुधार की आवश्यकता नहीं है। 'Specials.cpp' से फ़ंक्शन के चयन के रूप में मैं अनुमान लगाता हूं कि यह उस आदेश के कारण है जिसमें '.o' फ़ाइलें लिंकर को पास की जाती हैं; विशेष रूप से मुझे संदेह है कि 'specials.o' 'main.o' से पहले पारित किया गया है। –

+0

मुझे लगता है कि यह * बहुत * कार्यान्वयन विशिष्ट हिस्सा है। यह उस क्रम पर निर्भर करेगा जिसमें लिंकर * प्रक्रिया * ऑब्जेक्ट फाइलें, बदले में उस क्रम पर निर्भर करता है जिसमें ऑब्जेक्ट फ़ाइलें कमांड लाइन के माध्यम से पास की जाती हैं। और यह इस बात पर निर्भर हो सकता है कि लिंकर कैसे स्टोर करता है और दो टीयू में प्राप्त प्रतीकों को देखता है। यही कारण है कि कुछ भी हो सकता है, लेकिन मुझे लगता है कि वह भी पारित कर दिया सबसे स्पष्ट होगा पहली वस्तु फ़ाइलों पहले संसाधित, और पहली बार प्रतीक पाया जाता है है केवल एक ही संग्रहीत और कहा जाता हो जाता है। –

+0

ओह तुम ठीक कह रहे यह बहुत कार्यान्वयन विशिष्ट लगता है, लेकिन जब से जाहिरा तौर पर व्यवहार है * स्थिर *, वहाँ शायद एक व्याख्या है। –

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