2012-11-19 5 views
8

मैं वर्तमान में पढ़ रहा हूँ "प्रभावी सी ++" और वहाँ एक अध्याय है कि इस के लिए कोड समान होता है टाइपकास्ट करने के लिए एक टेम्पलेट वर्ग के लिए दोस्त को जोड़ने:सी ++ क्रम

template <typename T> 
class Num { 
public: 
    Num(int n) { ... } 
}; 

template <typename T> 
Num<T> operator*(const Num<T>& lhs, const Num<T>& rhs) { ... } 

Num<int> n = 5 * Num<int>(10); 

पुस्तक का कहना है कि इस नहीं होगा काम (और वास्तव में यह नहीं करता है) क्योंकि आप संकलक को टेम्पलेट विशेषज्ञ बनाने के लिए अंतर्निहित टाइपकास्टिंग का उपयोग करने की अपेक्षा नहीं कर सकते हैं।

एक हलचल के रूप में कक्षा के अंदर समारोह को परिभाषित करने के लिए "मित्र" वाक्यविन्यास का उपयोग करने का सुझाव दिया जाता है।

//It works 
template <typename T> 
class Num { 
public: 
    Num(int n) { ... } 

    friend 
    Num operator*(const Num& lhs, const Num& rhs) { ... } 
}; 

Num<int> n = 5 * Num<int>(10); 

और जब भी मुझे टेम्पलेट क्लास प्रकार में निहित रूपांतरण की आवश्यकता होती है तो पुस्तक इस मित्र-घोषणा की चीज़ का उपयोग करने का सुझाव देती है। और यह सब समझ में आता है।

लेकिन मैं एक सामान्य समारोह के साथ काम करने वाला एक ही उदाहरण क्यों नहीं प्राप्त कर सकता, ऑपरेटर नहीं?

template <typename T> 
class Num { 
public: 
    Num(int n) { ... } 

    friend 
    void doFoo(const Num& lhs) { ... } 
}; 

doFoo(5); 

इस बार संकलक शिकायतें कि उन्हें कोई भी 'डूफू' नहीं मिल रहा है। और यदि मैं कक्षा के बाहर डूफू घोषित करता हूं, तो मुझे उचित मिलान प्रकार की त्रुटि मिलती है। ऐसा लगता है कि "दोस्त ..." भाग को अनदेखा किया जा रहा है।

तो क्या मेरी समझ में कोई समस्या है? इस मामले में एक समारोह और ऑपरेटर के बीच क्या अंतर है?

+0

सूचना कैसे की 'ऑपरेटर *' कम से कम एक तर्क एक 'अंक ' होना चाहिए। यह 'doFoo' के लिए मामला नहीं है। आपका प्रकार 'int' से' int' के बिना 'int' से रचनात्मक हो सकता है। मजा यह है कि यह तब भी काम नहीं करेगा यदि आपके कनवर्ट करने वाले कन्स्ट्रक्टर क्लास टेम्पलेट के टेम्पलेट तर्कों के साथ पत्राचार करते हैं। आप उस टेम्पलेट के सदस्य फ़ंक्शन के माध्यम से क्लास टेम्पलेट तर्क को कम नहीं कर सकते हैं। आप 'make_num' फ़ंक्शन प्रदान करके उसके आस-पास जा सकते हैं। – pmr

उत्तर

3

कारण संकलक foo खोजने का कोई तरीका नहीं है कि यहाँ

doFoo(5); 

है, एक int पैरामीटर दिया। यह इस तरह अपने दोस्त ऑपरेटर बुला के बराबर होगा:

Num<int> n = 5 * 10; 

यह होगा "काम", लेकिन नहीं friend operator* अपने Num वर्ग में परिभाषित फोन करके, लेकिन फोन करके निर्मित पूर्णांकों के लिए operator*, और फिर Num के कनवर्टिंग कन्स्ट्रक्टर से अंतर्निहित रूपांतरण का उपयोग करना।

+0

यह वास्तव में कारण नहीं है, आप टेम्पलेट को हटाने और इस प्रकार टेम्पलेट तर्क को उसी मुद्दे को पुन: पेश कर सकते हैं। जवाब यह है कि लुकअप कैसे मिलता है (या खोजने में विफल रहता है) 'दोस्त' घोषणाएं। –

+0

@ डेविडरोड्रिगुएज़-ड्राईबीस ओके, यह समझ में आता है। तो यह एडीएल है, और यह विफल रहता है क्योंकि तर्क कक्षा नामस्थान में नहीं है। – juanchopanza

+0

की तरह। यह एडीएल है, लेकिन नामस्थान के साथ इसका कोई संबंध नहीं है जहां इसे परिभाषित किया गया है। लुकअप केवल एडीएल के माध्यम से एक मित्र घोषणा मिलेगा, लेकिन हम खोज में नामस्थान जोड़ने के बारे में बात नहीं कर रहे हैं, लेकिन * प्रकार *। जबकि अधिकांश लोग एडीएल के तर्कों के नामस्थान को लाने के बारे में सोचते हैं, यह उससे भी अधिक है: यह उन घोषणाओं की तलाश करता है जो केवल * अंदर * तर्कों के प्रकार उपलब्ध हैं, जो यहां है। –

0

हां ये कोड संकलक नहीं जानता कि इसके साथ कैसे काम करें। doFoo की तरह (5)

संकलक पता नहीं है 5 पूर्णांक

+2

कंपाइलर ** जानता है ** '5' एक int है। यह नहीं पता कि यह उस int से किसी प्रकार का 'न्यू ' बनाना है। – juanchopanza

1

है कोर समस्या देखने है। friend घोषणा नामस्थान स्तर के फ़ंक्शन की घोषणा प्रदान करती है, लेकिन घोषणा केवल कक्षा के अंदर उपलब्ध है जो इसे मित्रवत कर रही है। उदाहरण में पुस्तक प्रदान करता है कि यह कोई मुद्दा नहीं है: फ़ंक्शन संलग्नक प्रकार के दो तर्क लेता है, जब तक कि उनमें से एक संलग्न प्रकार का होता है, तर्कसंगत लुकअप कक्षा की परिभाषा के अंदर दिखेगा और ऑपरेटर को ढूंढेगा। आपके मामले में यह मामला नहीं है, क्योंकि एक भी तर्क है और उसे रूपांतरण की आवश्यकता है, संकलक कक्षा की परिभाषा के अंदर नहीं दिखेगा।

ध्यान दें कि यह टेम्पलेट्स और रूपांतरण की परवाह किए बिना है:

class A { 
    friend void f(int) {} 
    friend void g(int, A) {} 
}; 
int main() { 
    f(5);   // Error: lookup cannot find 'f' declared *only* inside A 
    g(5,A());  // Ok, one argument is 'A', lookup will find the function 
} 

मामले में उपरोक्त, जहां कोई शामिल टेम्पलेट्स देखते हैं, आपके पास संभावित घोषणा नाम स्थान स्तर पर इसे ठीक करने के लिए जोड़ सकते हैं, लेकिन यह नहीं है वास्तव में टेम्पलेट कक्षाओं के लिए एक विकल्प।

class A { 
    friend void f() { std::cout << "inside A\n"; } 
}; 
void f(int);  // only declaration 
int main() { 
    f(5);   // "inside A" 
} 

इस रूप में दोस्त घोषणा एक टेम्पलेट (और सभी instantiating प्रकार के लिए) के लिए नहीं किया जा सकता है एक गैर टेम्प्लेटेड समारोह की घोषणा। यद्यपि आप सिर्फ परीक्षण के लिए कोड के साथ खेल सकते हो सकता है:

template <typename T> 
struct Num { 
    Num(int x) ... 
    friend void f(Num const &); 
}; 
Num<int> f(Num<int> const &); // only declaration 
int main() { 
    f(5); 
}