2012-03-07 19 views
7

एक टेम्पलेट परिभाषा टेम्पलेट परिभाषा से मेल खाती है? मुझे मानक में कुछ पाठ टेम्पलेट-आईडी एक ही फ़ंक्शन का जिक्र करते हैं, यदि "उनके टेम्पलेट-नाम [...] उसी टेम्पलेट का संदर्भ लें और [...]" (14.4 [temp.type] पी 1) लेकिन मुझे टेम्पलेट-नाम या टेम्पलेट-नाम के लिए एक ही टेम्पलेट का संदर्भ नहीं मिल सकता है। मुझे यकीन नहीं है कि मैं सही रास्ते पर हूं या नहीं, क्योंकि मैंने व्याकरण को पर्याप्त रूप से नहीं बताया है कि टेम्पलेट-आईडी किसी टेम्पलेट की परिभाषा/घोषणा का हिस्सा है, या केवल एक का उपयोग है टेम्पलेट।टेम्पलेट घोषणाओं से टेम्पलेट परिभाषाएं कैसे मेल खाती हैं?

उदाहरण के लिए, निम्न प्रोग्राम ठीक काम करता है।

#include <iostream> 

template<typename T> 
T foo(T t); 

int main() { 
    foo(1); 
} 

template<typename T> 
T foo(T t) 
{ std::cout << "A\n"; return 0; } 

अगर मैं जिस तरह से मैं टेम्पलेट परिभाषा नाम जाहिरा तौर पर अब एक ही टेम्पलेट का उल्लेख में टेम्पलेट पैरामीटर का उपयोग बदलने के लिए, और जोड़ने में विफल रहता है।

#include <iostream> 

template<typename T> 
T foo(T t); 

int main() { 
    foo(1); 
} 

template<typename T> 
int foo(T t) { std::cout << "A\n"; return 0; } 

// or 

template<typename T> 
struct identity { 
    typedef T type; 
}; 

template<typename T> 
typename identity<T>::type 
foo(T t) { std::cout << "A\n"; return 0; } 

इसके बाद, अगर मैं एक और अनुवाद इकाई के लिए टेम्पलेट परिभाषा ले जाते हैं, सी ++ (MSVC 11 बीटा) के अपने कार्यान्वयन के लिए कार्यक्रम कोई फर्क नहीं पड़ता कि कैसे मैं प्रकार कहते हैं कि काम करता है।

//main.cpp 

template<typename T> 
T foo(T t); 

int main() { 
    foo(1); 
} 

//definition.cpp 
#include <iostream> 

template<typename T> 
struct identity { 
    typedef T type; 
}; 

template<typename T> 
typename identity<T>::type 
foo(T t) { std::cout << "A\n"; return 0; } 

template int foo<int>(int); 

या

//definition.cpp 
#include <iostream> 

template<typename T> 
int foo(T t) { std::cout << "A\n"; return 0; } 

template int foo<int>(int); 

या यहां तक ​​कि अगर परिभाषा एक टेम्पलेट बिल्कुल नहीं है:

//definition.cpp 
#include <iostream> 

int foo(T t) { std::cout << "A\n"; return 0; } 

जाहिर जोड़ने क्योंकि हस्ताक्षर/घायल नाम एक ही है सफलता मिल रही है की परवाह किए बिना टेम्पलेट जो प्रतीक बनाने के लिए तत्काल था। मैं इस अपरिभाषित व्यवहार क्योंकि मैं का उल्लंघन कर रहा हूँ लगता है:

§ 14.1 [अस्थायी] p6

एक समारोह टेम्पलेट, एक वर्ग टेम्पलेट के सदस्य समारोह, या स्थिर डेटा सदस्य एक वर्ग टेम्पलेट का करेगा प्रत्येक अनुवाद इकाई में परिभाषित किया गया है जिसमें यह तत्काल तत्काल (14.7.1) है जब तक कि संबंधित विशेषज्ञता स्पष्ट रूप से तत्काल (14.7.2) में कुछ अनुवाद इकाई है; कोई निदान की आवश्यकता नहीं है।

लेकिन फिर कहते हैं कि मैं दो स्थानों में से एक पर एक स्पष्ट इन्स्टेन्शियशन दूसरा अनुवाद इकाई में टेम्पलेट की एक परिभाषा डाल, और शामिल करके उन आवश्यकताओं को पूरा करने का प्रयास करें:

#include <iostream> 

template<typename T> 
T foo(T t) { std::cout << "A\n"; return 0; } 

// Location 1  

template<typename T> 
int foo(int t) { std::cout << "B\n"; return 0; } 

// Location 2 

क्या नियम हैं एक स्पष्ट तत्कालता किस टेम्पलेट को संदर्भित करता है, इस बारे में असंबद्धता के बारे में? इसे स्थान 1 पर रखकर सही टेम्पलेट को तत्काल बनाया जा सकता है और अंतिम परिभाषा में उस परिभाषा का उपयोग किया जाना चाहिए, जबकि इसे स्थान 2 पर डालने पर अन्य टेम्पलेट को तुरंत चालू किया जा सकता है, और जो मेरा मानना ​​है वह ऊपर 14.1 पी 6 के तहत अपरिभाषित व्यवहार है।

दूसरी ओर दो टेम्पलेट्स परिभाषाओं की एक अंतर्निहित इन्स्टेन्शियशन तो यह टेम्पलेट्स disambiguating के लिए शासन की तरह लगता है, कोई बात नहीं क्या पहला टेम्पलेट उठाता है इन परिस्थितियों में अलग है:

#include <iostream> 

template<typename T> 
T foo(T t) { std::cout << "A\n"; return 0; } 

template<typename T> 
int foo(int t) { std::cout << "B\n"; return 0; } 

int main() { 
    foo(1); // prints "A" 
} 

कारण यह आया ऊपर this question से संबंधित है जहां प्रश्नकर्ता है कि एक ही आगे घोषणा की खोज की

template<typename T> 
T CastScriptVarConst(const ScriptVar_t& s); 

कई टेम्प्लेट परिभाषाओं की एक घोषणा के रूप में कार्य नहीं कर सकता है:

template<typename T> 
typename std::enable_if<GetType<T>::value < SVT_BASEOBJ,T>::type 
CastScriptVarConst(const ScriptVar_t& s) { 
    return (T) s; 
} 

template<typename T> 
typename std::enable_if<!(GetType<T>::value < SVT_BASEOBJ) 
         && std::is_base_of<CustomVar,T>::value,T>::type 
CastScriptVarConst(const ScriptVar_t& s) { 
    return *s.as<T>(); 
} 

और मैं टेम्पलेट परिभाषाओं और घोषणाओं के बीच संबंधों को बेहतर ढंग से समझना चाहता था।

+0

पूर्ण कहानी के लिए "सी ++ टेम्पलेट्स: पूर्ण मार्गदर्शिका" की प्रतिलिपि उठाएं। और हाँ, यह मानक से अधिक पचाने योग्य है ... वास्तव में बहुत कुछ;) – 0xC0000022L

उत्तर

4

ठीक है, चलो शुरुआत से शुरू करते हैं। टेम्पलेट का "टेम्पलेट-नाम" फ़ंक्शन या क्लास का वास्तविक नाम टेम्पलेट किया गया है; वह है,

template<class T> T foo(T t); 

foo टेम्पलेट का नाम है। फ़ंक्शन टेम्पलेट्स के लिए, यह तय करने के लिए नियम कि वे समान हैं या नहीं, 14.5.5.1 "फ़ंक्शन टेम्पलेट ओवरलोडिंग" में वर्णित है। उस खंड के अनुच्छेद 6 (मैं यहां सी ++ 03 से उद्धरण दे रहा हूं, इसलिए शब्द और पैराग्राफ संख्याएं C++ 11 में बदल सकती हैं) समकक्ष और कार्यात्मक रूप से समतुल्य, टेम्पलेट से जुड़े अभिव्यक्तियों पर लागू होने पर परिभाषित करता है मापदंडों।

संक्षेप में, बराबर भाव संभवतः टेम्पलेट मापदंडों के लिए अलग-अलग नाम है, और कार्यात्मक रूप से बराबर भाव में यदि वे एक ही बात करने के लिए मूल्यांकन करने के लिए हो ही कर रहे हैं से अलग ही हैं। उदाहरण के लिए, पहले दो f घोषणाओं बराबर हैं, लेकिन तीसरे ही कार्यात्मक रूप से बराबर है अन्य दो: -

template<int A, int B> 
void f(array<A + B>); 
template<int T1, int T2> 
void f(array<T1 + T2>); 
template<int A, int B> 
void f(array< mpl::plus< mpl::int<A>, mpl::int<B> >::value >); 

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

template<typename T> 
T foo(T t); 

template<typename T> 
typename identity<T>::type foo(T t); 

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

स्पष्ट तत्कालता पर आगे बढ़ना, महत्वपूर्ण अनुभाग 14.7 है, "टेम्पलेट तत्काल और विशेषज्ञता"।अनुच्छेद 5 निम्न में से सभी की अनुमति नहीं देता:

  • स्पष्ट रूप से एक टेम्पलेट एक बार से अधिक instantiating;
  • स्पष्ट रूप instantiating और स्पष्ट रूप से एक ही टेम्पलेट विशेषज्ञता;
  • स्पष्ट रूप से एक बार से अधिक बहस का एक ही सेट के लिए एक टेम्पलेट विशेषज्ञता।

फिर, "कोई निदान की आवश्यकता है" के रूप में यह काफी पता लगाने के लिए मुश्किल है।

तो आपकी स्पष्ट इन्स्टेन्शियशन उदाहरण का विस्तार करने के लिए, निम्न कोड दूसरा नियम टूट जाता है और गैर कानूनी है: -

/* Template definition. */ 
template<typename T> 
T foo(T t) 
{ ... } 

/* Specialization, OK in itself. */ 
template< > 
int foo(int t) 
{ ... } 

/* Explicit instantiation, OK in itself. */ 
template< > 
int foo(int t); 

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

template<typename T> 
T f(T f) 
{ ... } 

template< > 
int f(int); 

void g(void) 
{ f(3); } 

लेकिन इस उदाहरण से अच्छी तरह से बनाई है, क्योंकि यह एक स्पष्ट इन्स्टेन्शियशन हैं: -

template<typename T> 
T f(T f) 
{ ... } 

template f(int); 

void g(void) 
{ f(3); } 

< > सभी अंतर बनाता है। यह चेतावनी भी दी है कि एक स्पष्ट विशेषज्ञता को परिभाषित भी जब आप ऐसा करेंगे, यह होने के लिए इससे पहले कि आप इसका इस्तेमाल करते हैं, अन्यथा संकलक पहले से ही उस टेम्पलेट के लिए एक अंतर्निहित इन्स्टेन्शियशन उत्पन्न हो सकता है है। इस 14.7.3 "स्पष्ट विशेषज्ञता" पैरा 6 में है, बस नीचे दिए तुम कहाँ पढ़ रहे थे, और फिर से, कोई निदान की आवश्यकता है। एक ही उदाहरण अनुकूल करने के लिए, इस बीमार बनाई है: -

template<typename T> 
T f(T f) 
{ ... } 

void g(void) 
{ f(3); } // Implicitly specializes int f(int) 

template< > 
int f(int) // Too late for an explicit specialization 
{ ... } 

यदि आपके पास पर्याप्त भ्रमित नहीं कर रहे थे फिर भी, अपने पिछले उदाहरण पर एक नज़र डालें: -

template<typename T> 
T foo(T t) { ... } 

template<typename T> 
int foo(int t) { ... } 

foo की दूसरी परिभाषा नहीं पहले परिभाषा के एक विशेषज्ञता है। यह template<typename T> T foo(T) की एक विशेषज्ञता होने के लिए template< > int foo(int) होना जरूरी होगा। लेकिन यह ठीक है: फ़ंक्शन ओवरलोडिंग की अनुमति है, और फ़ंक्शन टेम्पलेट्स और सामान्य कार्यों के बीच इसकी अनुमति है। फॉर्म foo(3) के कॉल हमेशा पहली परिभाषा का उपयोग करेंगे, क्योंकि इसका टेम्पलेट पैरामीटर T तर्क प्रकार से लिया जा सकता है। दूसरी परिभाषा तर्क टेम्पलेट से अपने टेम्पलेट पैरामीटर को कम करने की अनुमति नहीं देती है। केवल स्पष्ट रूप से T निर्दिष्ट करके आप दूसरी परिभाषा तक पहुँच सकते हैं, और उसके बाद ही जब कॉल पहली परिभाषा के साथ अस्पष्ट नहीं है: -

f<int>(3); // ambiguous 
f<string>(3); // can only be the second one 

समारोह टेम्पलेट्स के लिए अधिभार संकल्प करने का पूरी प्रक्रिया बहुत लंबा यहाँ वर्णन करने के लिए है । यदि आप रुचि रखते हैं और अधिक प्रश्न पूछें तो अनुभाग 14.8.3 पढ़ें :-)

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