2009-08-08 11 views
10

मैं SFINAE पर विकिपीडिया लेख पढ़ रहा था और का सामना करना पड़ा निम्नलिखित कोड नमूना:आपको कभी-कभी 'टी' के बजाय 'टाइपनाम टी' लिखने की आवश्यकता क्यों होती है?

struct Test 
{ 
    typedef int Type; 
}; 

template < typename T > 
void f(typename T::Type) {} // definition #1 

template < typename T > 
void f(T) {}    // definition #2 

void foo() 
{ 
    f<Test> (10); //call #1 

    f<int> (10); //call #2 without error thanks to SFINAE 
} 
अब मैं वास्तव में इस तरह कोड लिखा है पहले, और किसी भी तरह सहज मैं जानता था कि मैं टाइप करने के लिए "typename टी" के बजाय जरूरत

बस टी"। हालांकि, इसके पीछे वास्तविक तर्क जानना अच्छा लगेगा। किसी को भी समझाने की परवाह है?

+1

मैं आपको टेम्पलेट को पढ़ने की सलाह देता हूं: http://womble.decadentplace.org.uk/c++/template-faq.html –

उत्तर

8

सामान्य रूप से, सी ++ के वाक्यविन्यास (सी से विरासत में मिला) में तकनीकी दोष होता है: पार्सर को पता होना चाहिए कि कुछ प्रकार का नाम है या नहीं, अन्यथा यह केवल कुछ अस्पष्टताओं को हल नहीं कर सकता है (उदाहरण के लिए, X * Y गुणा है, या एक्स एक्स की ऑब्जेक्ट्स के लिए पॉइंटर वाई की घोषणा? यह सब इस बात पर निर्भर करता है कि एक्स एक प्रकार का नाम है ...! -)। typename "विशेषण" आपको आवश्यकता होने पर पूरी तरह स्पष्ट और स्पष्ट करने देता है (जो, जैसा कि एक और उत्तर उल्लेख है, विशिष्ट है जब टेम्पलेट पैरामीटर शामिल हैं ;-)।

+1

क्या आपका मतलब है "आम तौर पर जब टेम्पलेट पैरामीटर शामिल होते हैं" (इस मामले में मुझे पूरा यकीन है कि यह केवल * केवल * जब टेम्पलेट पैरामीटर शामिल होते हैं), या यह एक टाइपो है जो "टेम्पलेट पैरामीटर शामिल होने पर सामान्य है" ? –

+1

@onebyone, अच्छी पकड़, मैं वास्तव में "ठेठ" और "आम तौर पर" नहीं था, ठीक करने के लिए मेरा जवाब संपादित, टीएक्स। –

13

जब भी एक्स टेम्पलेट पैरामीटर पर है या निर्भर करता है तो आपको छोटा संस्करण typename X::Y करने की आवश्यकता है। जब तक एक्स ज्ञात नहीं है, संकलक यह नहीं बता सकता कि वाई एक प्रकार या मान है या नहीं। इसलिए आपको यह निर्दिष्ट करने के लिए typename जोड़ना होगा कि यह एक प्रकार है।

उदाहरण के लिए:

template <typename T> 
struct Foo { 
    typename T::some_type x; // T is a template parameter. `some_type` may or may not exist depending on what type T is. 
}; 

template <typename T> 
struct Foo { 
    typename some_template<T>::some_type x; // `some_template` may or may not have a `some_type` member, depending on which specialization is used when it is instantiated for type `T` 
}; 

एसबीआई टिप्पणी में बताते हैं के रूप में, अस्पष्टता के कारण कि Y एक स्थिर सदस्य, एक enum या एक समारोह हो सकता है। X के प्रकार को जानने के बिना, हम नहीं बता सकते हैं। मानक निर्दिष्ट करता है कि संकलक को यह मानना ​​चाहिए कि यह typename कीवर्ड का उपयोग कर स्पष्ट रूप से किसी प्रकार का लेबल नहीं है।

और यह लग रहा है टिप्पणीकर्ताओं वास्तव में मुझे एक और संबंधित मामले का उल्लेख करने के साथ ही चाहते हैं की तरह:;)

निर्भर नाम एक समारोह सदस्य टेम्पलेट है, और आप एक स्पष्ट टेम्पलेट तर्क (foo.bar<int>() से कॉल करने, के लिए उदाहरण), आपको foo.template bar<int>() में फ़ंक्शन नाम से पहले template कीवर्ड जोड़ना होगा।

इसका कारण यह है कि टेम्पलेट कीवर्ड के बिना, कंपाइलर मानता है कि bar एक मान है, और आप ऑपरेटर (operator<) से कम इसे कम करना चाहते हैं।

+0

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

+0

'टेम्पलेट' हालांकि एक ही तर्क नहीं है। इसका आश्रित नामों से कोई लेना-देना नहीं है। यह "समस्याएं जो आपको टेम्पलेट्स के साथ यात्रा करने के लिए प्रवृत्त करती हैं" की श्रेणी में पड़ती है, लेकिन मुझे नहीं लगता कि यह 'टाइपनाम' जैसा ही कारण है। – jalf

+1

@jalf: क्या यह टेम्पलेट नामों के साथ नहीं है? किसी भी मामले में, कारण काफी समान हैं। इसे केवल टेम्पलेट कोड में ही अनुमति दी गई है और इसे आश्रित सदस्य नामों में अंतर करने के लिए उपयोग किया जाना चाहिए जो आश्रित सदस्य नामों से टेम्पलेट के नाम हैं। –

3

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

अपने उदाहरण में, आप परिभाषा # 1 पर typename T::Type का उपयोग करते हैं क्योंकि T::Type टेम्पलेट पैरामीटर T पर निर्भर करता है और अन्यथा डेटा सदस्य हो सकता है।

आपको typename T की परिभाषा # 2 के रूप में T को टेम्पलेट परिभाषा के हिस्से के रूप में घोषित करने की आवश्यकता नहीं है।