2011-03-20 9 views
44

निम्नलिखित स्निपेट त्रुटि संकलन के दौरान "foo को ambigious कॉल" एक पैदा करता है, और मैं पूरी तरह से कॉल योग्यता foo के बिना अगर वहाँ किसी भी तरह से इस समस्या को हल है जानना चाहते हैं:एक ही नाम के साथ एकाधिक विरासत वाले फ़ंक्शन क्यों करते हैं लेकिन अलग-अलग हस्ताक्षर ओवरलोड किए गए फ़ंक्शंस के रूप में नहीं मानते हैं?

#include <iostream> 

struct Base1{ 
    void foo(int){ 
    } 
}; 

struct Base2{ 
    void foo(float){ 
    } 
}; 

struct Derived : public Base1, public Base2{ 
}; 

int main(){ 
    Derived d; 
    d.foo(5); 

    std::cin.get(); 
    return 0; 
} 

तो, सवाल शीर्षक के रूप में है। विचार? मेरा मतलब है, निम्नलिखित कार्य निर्दोष रूप से काम करते हैं:

#include <iostream> 

struct Base{ 
    void foo(int){ 
    } 
}; 

struct Derived : public Base{ 
    void foo(float){ 
    } 
}; 

int main(){ 
    Derived d; 
    d.foo(5); 

    std::cin.get(); 
    return 0; 
} 
+2

लगातार दो फ़ूज़ (दूसरे मामले में) के अंदर लॉगिंग स्टेटमेंट्स को जोड़ने के लिए जो फ़ंक्शन कहा जाता है, आप आश्चर्यचकित होंगे ... सी ++ आर्केन नियम से भरा है;) –

+1

@Maththieu: * gasp *! डॉन छुपा नियम। :( – Xeo

उत्तर

42

सदस्य देखने नियम अनुभाग में परिभाषित कर रहे हैं 10,2/2

निम्न चरणों का पालन, एक वर्ग दायरे में नाम देखने के परिणाम को परिभाषित C। सबसे पहले, कक्षा में नाम और प्रत्येक बेस श्रेणी उप-वस्तुओं में नाम के लिए प्रत्येक घोषणा माना जाता है। f एक उप-ऑब्जेक्ट B में उप-ऑब्जेक्ट A में सदस्य नाम f छुपाता है यदि AB का बेस क्लास उप-ऑब्जेक्ट है। किसी भी घोषणा जो इतनी छुपी हुई हैं पर विचार से हटा दी गई है। इन घोषणाओं में से प्रत्येक घोषणा जिसे घोषणा-घोषणा द्वारा पेश किया गया था, को C के प्रत्येक उप-ऑब्जेक्ट से माना जाता है, जिसमें उपयोग-घोषणा द्वारा निर्दिष्ट घोषित-टयन युक्त प्रकार है। यदि घोषणाओं का परिणामी सेट एक ही प्रकार की उप-वस्तुओं से नहीं है, या सेट में एक गैरस्टिक सदस्य है और इसमें अलग-अलग उप-वस्तुओं के सदस्य शामिल हैं, तो अस्पष्टता है और कार्यक्रम खराब हो गया है। अन्यथा यह सेट लुकअप का परिणाम है।

class A { 
public: 
    int f(int); 

}; 
class B { 
public: 
    int f(); 

}; 
class C : public A, public B {}; 
int main() 
{ 
    C c; 
    c.f(); // ambiguous 
} 

तो आपको लगता है कि अस्पष्टता

class C : public A, public B { 
    using A::f; 
    using B::f; 

}; 

int main() 
{ 
    C c; 
    c.f(); // fine 
} 

दूसरा कोड दोषरहित काम करता है क्योंकि void foo(float) सी गुंजाइश अंदर है हल करने using घोषणाओं A::f और B::f उपयोग कर सकते हैं। दरअसल d.foo(5);void foo(float) पर कॉल करता है और int संस्करण नहीं।

+2

'शून्य फू (फ्लोट)' संस्करण को वास्तव में मुझे वहां पहुंचाया गया है .. आपके व्यापक उत्तर के लिए धन्यवाद। :) – Xeo

+1

एक बात जो दिमाग में आती है ... क्या कोई ऐसी स्थिति है, जहां कोई छिपाना चाहता है बेस क्लास फ़ंक्शंस यदि उनके पास अलग-अलग हस्ताक्षर हैं? एक ही हस्ताक्षर कार्यों के लिए, सुनिश्चित करें, यह उपयोगी है, लेकिन अलग-अलग लोगों के लिए मैं सिर्फ एक अच्छा उदाहरण कल्पना नहीं कर सकता ... – Xeo

+0

+1। बहुत अच्छा। मुझे यह नहीं पता था। धन्यवाद प्रसुण। :-) – Nawaz

2

क्या यह आपके लिए काम करेगा?

struct Derived : public Base1, public Base2{ 
    using Base2::foo;} 
2

नाम लुकअपअधिभार संकल्प पर एक अलग चरण है।

नाम लुकअप पहले होता है। यह तय करने की प्रक्रिया है कि नाम किस दायरे पर लागू होता है। इस मामले में हमें यह तय करना होगा कि d.foo का अर्थ है d.D::foo, या d.B1::foo, या d.B2::foo। नाम लुकअप नियम खाता फ़ंक्शन पैरामीटर या कुछ भी नहीं लेते हैं; यह पूरी तरह से नाम और scopes के बारे में है।

केवल एक बार निर्णय लेने के बाद, क्या हम उस क्षेत्र के विभिन्न अधिभारों पर ओवरलोड रिज़ॉल्यूशन करते हैं जहां नाम पाया गया था।

आपके उदाहरण में, पर कॉल करने से D::foo() मिल जाएगा यदि ऐसा कोई फ़ंक्शन था। लेकिन कोई नहीं है। तो, स्कॉप्स के पीछे पीछे काम करते हुए, यह बेस क्लास की कोशिश करता है। अब foo समान रूप से B1::foo या B2::foo पर देख सकता है, इसलिए यह संदिग्ध है।

इसी कारण से, आपको D सदस्य फ़ंक्शन के अंदर अयोग्य foo(5); पर अस्पष्टता मिल जाएगी।


सुझाए गए समाधान का प्रभाव:

struct Derived : public Base1, public Base2{ 
    using Base1::foo; 
    using Base2::foo; 

कि इस नाम D::foo बनाता है, और यह दो कार्य की पहचान करता है। नतीजा यह है कि d.food.D::foo पर हल करता है, और फिर इन दो कार्यों पर अधिभार संकल्प हो सकता है जिन्हें D::foo द्वारा पहचाना जाता है।

नोट: इस उदाहरण में D::foo(int) और Base1::foo(int) एक फ़ंक्शन के लिए दो पहचानकर्ता हैं; लेकिन सामान्य रूप से, नाम लुकअप और ओवरलोड रिज़ॉल्यूशन प्रक्रिया के लिए, इससे कोई फर्क नहीं पड़ता कि वे दो अलग-अलग फ़ंक्शन हैं या नहीं।