2009-10-02 19 views
20

तो, मैं जानता हूँ कि कोड के इन दो छोटी-मोटी बातें के बीच एक अंतर है कि:टेम्पलेट विशेषज्ञता और कार्यों के लिए ओवरलोडिंग के बीच मतभेद?

template <typename T> 
T inc(const T& t) 
{ 
    return t + 1; 
} 

template <> 
int inc(const int& t) 
{ 
    return t + 1; 
} 

और

template <typename T> 
T inc(const T& t) 
{ 
    return t + 1; 
} 

int inc(const int& t) 
{ 
    return t + 1; 
} 

मैं क्या इन दोनों के बीच कार्यात्मक मतभेद हैं के रूप में उलझन में हूँ। क्या कोई ऐसी स्थितियों को दिखा सकता है जहां ये स्निपिट एक-दूसरे से अलग-अलग कार्य करते हैं?

+0

मैंने सवाल उठाया क्योंकि यह सी ++ (कम से कम मेरे लिए) में अंधेरे क्षेत्रों में से एक है! – AraK

+5

आप विशेष रूप से क्या पूछते हैं? बस कुछ अंतर, या कुछ मामलों में जहां खतरे की जगह है? उदाहरण के लिए, निश्चित रूप से 'inc <> (10);' इन दो स्निपेट के लिए अलग व्यवहार करता है - लेकिन क्या आप यह सुनना चाहते हैं? –

उत्तर

14

मैं केवल कुछ अंतरों के बारे में सोच सकता हूं - यहां कुछ उदाहरण हैं जो आवश्यक रूप से नुकसान (मुझे लगता है) नहीं है। मैं इसे

template <typename T> T inc(const T& t); 
namespace G { using ::inc; } 
template <> int inc(const int& t); 
namespace G { void f() { G::inc(10); } } // uses explicit specialization 

// --- against --- 

template <typename T> T inc(const T& t); 
namespace G { using ::inc; } 
int inc(const int& t); 
namespace G { void f() { G::inc(10); } } // uses template 

है ऐसा इसलिए है क्योंकि विशेषज्ञताओं नाम देखने से नहीं मिला रहे हैं संक्षिप्त रखने के लिए परिभाषाओं को छोड़ते हुए हूँ, लेकिन तर्क से मेल खाते से, तो एक का उपयोग कर घोषणा स्वचालित रूप से एक बाद में शुरू विशेषज्ञता पर विचार करेगी।

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

template <typename T> void f(T t); // called for non-pointers 
template <typename T> void f(T *t); // called for pointers. 

int a; 
void e() { 
    f(a); // calls the non-pointer version 
    f(&a); // calls the pointer version 
} 

कि समारोह टेम्पलेट स्पष्ट विशेषज्ञता के साथ संभव नहीं होगा।

template<typename T> void f(T const &); 
template<> void f(int * const &); 

template<typename T> void g(T const &); 
void g(int * const &); 

int a[5]; 
void e() { 
    // calls the primary template, not the explicit specialization 
    // because `T` is `int[5]`, not `int *` 
    f(a); 

    // calls the function, not the template, because the function is an 
    // exact match too (pointer conversion isn't costly enough), and it's 
    // preferred. 
    g(a); 
} 

मेरा सुझाव है कि आप हमेशा उपयोग करने के लिए: एक और उदाहरण है जब संदर्भ शामिल कर रहे हैं, जिसकी वजह से शामिल (सापेक्ष आधार/व्युत्पन्न वर्ग रिश्ते और constness) प्रकार के सटीक मिलान के लिए देखने के लिए टेम्पलेट तर्क कटौती है ओवरलोडिंग, क्योंकि यह अमीर है (कुछ आंशिक विशेषज्ञता की अनुमति देता है), और इसके अलावा आप जो भी नेमस्पेस चाहते हैं उसमें फ़ंक्शन डाल सकते हैं (हालांकि तब यह कड़ाई से ओवरलोडिंग नहीं है)। उदाहरण के लिए, std:: नामस्थान में विशेषज्ञता रखने के बजाय, आप अपने swap अधिभार को अपने नामस्थान में रख सकते हैं और इसे एडीएल द्वारा कॉल करने योग्य बना सकते हैं।

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

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

+0

ग्रेट आलेख। । – rlbond

+0

+1 अच्छा - मुझे पसंद है :) –

+0

धन्यवाद दोस्तों :) –

5

टेम्पलेट विशेषज्ञता केवल ओवरलोडिंग से अधिक सामान्य है। आप साधारण कार्यों की बजाय कक्षाओं जैसी चीजों को विशेषज्ञ बना सकते हैं। अधिभार केवल कार्यों पर लागू होता है।

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

तो, टेम्पलेट विशेषज्ञता का लक्ष्य फ़ंक्शन अधिभार से काफी अलग है। वे मूल रूप से अलग होने पर आपके उदाहरण में समान व्यवहार करते हैं।

यदि आप अधिभार प्रदान करते हैं, तो आप एक स्वतंत्र विधि घोषित कर रहे हैं जो समान नाम होता है। आप टेम्पलेट को विशिष्ट प्रकार पैरामीटर के साथ उपयोग करने से नहीं रोक रहे हैं। इस तथ्य को प्रदर्शित करने के लिए, कोशिश: inc(const int&) और inc<int>(const int&):

template <typename T> 
T inc(const T& t) 
{ 
    return t + 1; 
} 

int inc(const int& t) 
{ 
    return t + 42; 
} 

#include <iostream> 
int main() { 
    int x = 0; 
    x = inc<int>(x); 
    std::cout << "Template: " << x << std::endl; // prints 1. 

    x = 0; 
    x = inc(x); 
    std::cout << "Overload: " << x << std::endl; // prints 42. 
} 

आप देख सकते हैं, इस उदाहरण में, वहाँ int मूल्यों के लिए दो अलग-अलग inc कार्य हैं। यदि आपने टेम्पलेट विशेषज्ञता का उपयोग किया था तो आप int का उपयोग करके सामान्य टेम्पलेट का विस्तार नहीं कर सके।

+2

अच्छा नोट, हालांकि यह सवाल का जवाब नहीं देता है। – AraK

+1

अरक: प्रश्न, जैसा कि मैं इसे समझता हूं, मूल रूप से, एक ही चीज़ को प्राप्त करने के दो तरीके क्यों हैं। यह दो तरीकों से प्राप्त एक समान परिणाम दिखाता है और दो तरीकों के बीच अंतर मांगता है। मेरा जवाब यह है कि ये दो विधियां अलग-अलग संदर्भों पर लागू होती हैं और एक उदाहरण जहां दोनों लागू होते हैं, उन्हें समकक्ष नहीं बनाते हैं। –

+0

मैंने कहा कि यह एक अच्छा नोट है, लेकिन * फ़ंक्शन टेम्पलेट विशेषज्ञता * और * फ़ंक्शन ओवरलोडिंग * के बीच कार्यक्षमता में अंतर का उत्तर नहीं देता है। आप आम तौर पर टेम्पलेट विशेषज्ञता के बारे में बात कर रहे हैं। मुझे लगता है कि शीर्षक थोड़ा भ्रामक है। – AraK

1

AFAIK कोई कार्यात्मक अंतर नहीं है। मैं जो भी जोड़ सकता हूं वह यह है कि यदि आपके पास एक टेम्पलेट फ़ंक्शन विशेषज्ञता और सामान्य कार्य परिभाषित किया गया है तो सामान्य कार्य के अनुकूल होने के कारण कोई अधिभार अस्पष्टता नहीं है। इस तरह के

4

एक उदाहरण:

#include <cstdio> 

template <class T> 
void foo(T) 
{ 
    puts("T"); 
} 

//template <> 
void foo(int*) 
{ 
    puts("int*"); 
} 

template <class T> 
void foo(T*) 
{ 
    puts("T*"); 
} 

int main() 
{ 
    int* a; 
    foo(a); 
} 

यह वास्तव में सुझाव दिया है कि आप कार्यों के लिए गैर टेम्पलेट भार के इस्तेमाल करते हैं और कक्षाओं के लिए विशेषज्ञता छोड़ दें। Why Not Specialize Function Templates?

1

बस answer में litb द्वारा उल्लिखित पहले बिंदु पर विस्तृत करने के लिए। विशेषज्ञता केवल तभी जांच की जाती है जब ओवरलोड रिज़ॉल्यूशन ने वास्तव में प्राथमिक टेम्पलेट का चयन किया हो। परिणाम कुछ आश्चर्य जहां एक समारोह अतिभारित और स्पष्ट विशेषज्ञताओं है करने के लिए नेतृत्व कर सकते हैं:

template <typename T> void foo (T); // Primary #1 
template <> void foo<int*> (int*); // Specialization of #1 

template <typename T> void foo (T*); // Primary #2 

void bar (int * i) 
{ 
    foo(i); 
} 

का चयन करते समय जो कॉल करने के लिए कार्य करते हैं, तो निम्न चरणों जगह ले:

  1. नाम देखने प्राथमिक टेम्पलेट्स पाता है।
  2. प्रत्येक टेम्पलेट विशिष्ट और अधिभार संकल्प तर्क और पैरामीटर के बीच रूपांतरणों के आधार पर सर्वोत्तम फ़ंक्शन का चयन करने का प्रयास करता है।
  3. इस मामले में, रूपांतरणों की गुणवत्ता में कोई अंतर नहीं है।
  4. आंशिक क्रम नियमों का उपयोग तब सबसे विशिष्ट टेम्पलेट का चयन करने के लिए किया जाता है। इस मामले में यह दूसरा पैरामीरी "फू (टी *)" है।

केवल इन चरणों का के बाद, जब सबसे अच्छा समारोह में चुना गया है चयनित समारोह की स्पष्ट विशेषज्ञताओं विचार किया जाएगा। (इस मामले में प्राथमिक # 2 में कोई नहीं है इसलिए कोई भी विचार नहीं किया जाता है)।

void bar (int * i) 
{ 
    foo<int*> (i); // expliit template argument forces use of primary #1 
} 

इसका एक अच्छा शासन भार के वह भी explicily विशेष कर रहे हैं से बचने के लिए प्रयास करने के लिए है:

ऊपर स्पष्ट विशेषज्ञता यहां कॉल करने के लिए एक ही रास्ता है, वास्तव में कॉल में स्पष्ट टेम्पलेट तर्कों का उपयोग करने के लिए है , जिसके परिणामस्वरूप नियम बहुत जटिल हैं।

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