खोज…


टिप्पणियों

ओवरलोड रिज़ॉल्यूशन कई अलग-अलग स्थितियों में होता है

  • अतिभारित कार्यों को नाम दिया गया है। उम्मीदवार नाम लुकअप द्वारा पाए गए सभी कार्य हैं।
  • वर्ग वस्तु की ओर इशारा करता है। उम्मीदवार आमतौर पर वर्ग के सभी अतिभारित फ़ंक्शन कॉल ऑपरेटर होते हैं।
  • एक ऑपरेटर का उपयोग। उम्मीदवार नेमस्पेस स्कोप में ओवरलोडेड ऑपरेटर फ़ंक्शंस, लेफ्ट क्लास ऑब्जेक्ट में ओवरलोडेड ऑपरेटर फ़ंक्शंस (यदि कोई हो) और बिल्ट-इन ऑपरेटर्स हैं।
  • अधिभार के लिए आह्वान करने के लिए सही रूपांतरण ऑपरेटर फ़ंक्शन या कंस्ट्रक्टर को खोजने के लिए अधिभार संकल्प
    • गैर-सूची प्रत्यक्ष आरंभीकरण ( Class c(value) ) के लिए, उम्मीदवार Class निर्माता हैं।
    • गैर-सूची प्रतिलिपि आरंभीकरण ( Class c = value ) के लिए और उपयोगकर्ता परिभाषित रूपांतरण अनुक्रम को उपयोगकर्ता परिभाषित रूपांतरण अनुक्रम में खोजने के लिए। उम्मीदवार Class के निर्माता हैं और यदि स्रोत एक क्लास ऑब्जेक्ट है, तो इसका रूपांतरण ऑपरेटर कार्य करता है।
    • एक वर्ग वस्तु से एक गैर-वर्ग के आरंभीकरण के लिए ( Nonclass c = classObject )। उम्मीदवार इनिशियलाइज़र ऑब्जेक्ट के रूपांतरण ऑपरेटर फ़ंक्शन हैं।
    • किसी क्लास ऑब्जेक्ट ( R &r = classObject ) के साथ एक संदर्भ को इनिशियलाइज़ करने के लिए, जब क्लास में कन्वर्जन ऑपरेटर फ़ंक्शंस होते हैं, जो ऐसे वैल्यूज़ देते हैं जो सीधे r से बाउंड हो सकते हैं। उम्मीदवार ऐसे रूपांतरण ऑपरेटर कार्य हैं।
    • गैर-एग्रीगेट क्लास ऑब्जेक्ट ( Class c{1, 2, 3} ) की सूची-आरंभीकरण के लिए, उम्मीदवार ओवरलोड रिज़ॉल्यूशन के माध्यम से पहली बार पास करने के लिए शुरुआती सूची निर्माता हैं। यदि यह एक व्यवहार्य उम्मीदवार नहीं ढूंढता है, तो ओवरलोड रिज़ॉल्यूशन के माध्यम से एक दूसरा पास किया जाता है, Class के कंस्ट्रक्टर्स के साथ उम्मीदवारों के रूप में।

सटीक मिलान

पैरामीटर प्रकारों के लिए आवश्यक रूपांतरणों के बिना एक अधिभार या केवल सटीक मिलान समझे जाने वाले प्रकारों के बीच आवश्यक रूपांतरणों को अधिभार के लिए पसंद किया जाता है जिसे कॉल करने के लिए अन्य रूपांतरणों की आवश्यकता होती है।

void f(int x);
void f(double x);
f(42); // calls f(int)

जब एक तर्क एक ही प्रकार के संदर्भ में बांधता है, तो मैच को एक रूपांतरण की आवश्यकता नहीं माना जाता है, भले ही संदर्भ अधिक सीवी-योग्य हो।

void f(int& x);
void f(double x);
int x = 42;
f(x); // argument type is int; exact match with int&

void g(const int& x);
void g(int x);
g(x); // ambiguous; both overloads give exact match

ओवरलोड रिज़ॉल्यूशन के प्रयोजनों के लिए, प्रकार " T सरणी" को "पॉइंटर टू T " के प्रकार के साथ सटीक रूप से मेल खाने के लिए माना जाता है, और फ़ंक्शन प्रकार T को फ़ंक्शन पॉइंटर प्रकार T* साथ बिल्कुल मेल खाने के लिए माना जाता है, भले ही दोनों की आवश्यकता हो रूपांतरण।

void f(int* p);
void f(void* p);

void g(int* p);
void g(int (&p)[100]);

int a[100];
f(a); // calls f(int*); exact match with array-to-pointer conversion
g(a); // ambiguous; both overloads give exact match

पैरामीटर लागत के लिए तर्क का वर्गीकरण

ओवरलोड रिज़ॉल्यूशन एक तर्क को चार अलग-अलग श्रेणी में से एक पैरामीटर में पारित करने की लागत को विभाजित करता है, जिसे "अनुक्रम" कहा जाता है। प्रत्येक अनुक्रम में शून्य, एक या कई रूपांतरण शामिल हो सकते हैं

  • मानक रूपांतरण अनुक्रम

    void f(int a); f(42);
    
  • उपयोगकर्ता परिभाषित रूपांतरण अनुक्रम

    void f(std::string s); f("hello");
    
  • एलिप्सिस रूपांतरण अनुक्रम

    void f(...); f(42);
    
  • सूची आरम्भ क्रम

    void f(std::vector<int> v); f({1, 2, 3});
    

सामान्य सिद्धांत यह है कि मानक रूपांतरण अनुक्रम सबसे सस्ते हैं, इसके बाद उपयोगकर्ता परिभाषित रूपांतरण अनुक्रम होते हैं, इसके बाद दीर्घवृत्त रूपांतरण अनुक्रम होते हैं।

एक विशेष मामला सूची आरंभीकरण अनुक्रम है, जो रूपांतरण का गठन नहीं करता है (एक प्रारंभिक सूची एक प्रकार के साथ एक अभिव्यक्ति नहीं है)। इसकी लागत को निर्धारित करने के लिए निर्धारित किया जाता है, जो कि अन्य तीन रूपांतरण अनुक्रमों में से एक के बराबर है, जो कि इनिशियलाइज़र सूची के पैरामीटर प्रकार और रूप पर निर्भर करता है।

नाम लुकअप और एक्सेस चेकिंग

नाम लुकअप के बाद अधिभार संकल्प होता है। इसका मतलब यह है कि बेहतर लुक-अप फ़ंक्शन को ओवरलोड रिज़ॉल्यूशन द्वारा नहीं चुना जाएगा यदि यह नाम लुकअप खो देता है:

void f(int x);
struct S {
    void f(double x);
    void g() { f(42); } // calls S::f because global f is not visible here,
                        // even though it would be a better match
};

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

class C {
  public:
    static void f(double x);
  private:
    static void f(int x);
};
C::f(42); // Error! Calls private C::f(int) even though public C::f(double) is viable.

इसी तरह, ओवरलोड रिज़ॉल्यूशन जाँच के बिना होता है कि परिणामी कॉल explicit :

struct X {
    explicit X(int );
    X(char );
};

void foo(X );
foo({4}); // X(int) is better much, but expression is 
          // ill-formed because selected constructor is explicit

अग्रेषण संदर्भ पर ओवरलोडिंग

अग्रेषण संदर्भ अधिभार प्रदान करते समय आपको बहुत सावधान रहना चाहिए क्योंकि यह बहुत अच्छी तरह से मेल खा सकता है:

struct A {
    A() = default;           // #1
    A(A const& ) = default;  // #2

    template <class T>
    A(T&& );                 // #3
};

यहाँ आशय यह था कि A प्रतिलिपि योग्य है, और हमारे पास यह अन्य निर्माता है जो किसी अन्य सदस्य को इनिशियलाइज़ कर सकता है। तथापि:

A a;     // calls #1
A b(a);  // calls #3!

निर्माण कॉल के लिए दो व्यवहार्य मैच हैं:

A(A const& ); // #2
A(A& );       // #3, with T = A&

दोनों सटीक मिलान कर रहे हैं, लेकिन #3 तुलना में एक कम सीवी -qualified ऑब्जेक्ट के संदर्भ लेता है #2 करता है, तो यह बेहतर मानक रूपांतरण अनुक्रम है और सबसे अच्छा व्यवहार्य कार्य है।

यहाँ समाधान हमेशा इन कंस्ट्रक्टरों को बनाने के लिए है (जैसे कि SFINAE का उपयोग करके):

template <class T,
    class = std::enable_if_t<!std::is_convertible<std::decay_t<T>*, A*>::value>
    >
A(T&& );

यहाँ टाइप गुण किसी A या वर्ग को सार्वजनिक रूप से और असंदिग्ध रूप से A से विचारणीय है, जो इस निर्माता को पहले वर्णित उदाहरण में बीमार बना देगा (और इसलिए अधिभार सेट से हटा दिया गया है)। नतीजतन, कॉपी निर्माता को आमंत्रित किया जाता है - जो कि हम चाहते थे।

अधिभार संकल्प के कदम

अधिभार संकल्प के चरण हैं:

  1. नाम देखने के माध्यम से उम्मीदवार कार्यों का पता लगाएं। अयोग्य कॉल नियमित रूप से अयोग्य लुकअप के साथ-साथ तर्क-निर्भर लुकअप (यदि लागू हो) दोनों का प्रदर्शन करेगी।

  2. व्यवहार्य कार्यों के एक सेट के लिए उम्मीदवार कार्यों के सेट को फ़िल्टर करें। एक व्यवहार्य फ़ंक्शन जिसके लिए फ़ंक्शन के तर्कों के बीच एक अंतर्निहित रूपांतरण अनुक्रम मौजूद होता है जिसे फ़ंक्शन कहा जाता है और पैरामीटर लेता है।

    void f(char);          // (1)
    void f(int ) = delete; // (2)
    void f();              // (3)
    void f(int& );         // (4)
    
    f(4); // 1,2 are viable (even though 2 is deleted!) 
          // 3 is not viable because the argument lists don't match
          // 4 is not viable because we cannot bind a temporary to 
          //     a non-const lvalue reference
    
  3. सबसे अच्छा व्यवहार्य उम्मीदवार चुनें। एक व्यवहार्य फ़ंक्शन F1 एक अन्य व्यवहार्य फ़ंक्शन F2 तुलना में एक बेहतर कार्य है यदि F1 में प्रत्येक तर्क के लिए अंतर्निहित रूपांतरण अनुक्रम F2 में संबंधित अंतर्निहित रूपांतरण अनुक्रम से भी बदतर नहीं है, और ...:

    3.1। कुछ तर्क के लिए, F1 में उस तर्क के लिए निहित रूपांतरण अनुक्रम F2 , या में उस तर्क के लिए एक बेहतर रूपांतरण अनुक्रम है

    void f(int );  // (1)
    void f(char ); // (2)
    
    f(4);  // call (1), better conversion sequence
    

    3.2। उपयोगकर्ता-परिभाषित रूपांतरण में, F1 की वापसी से गंतव्य प्रकार तक मानक रूपांतरण अनुक्रम F1 F2 के वापसी प्रकार की तुलना में बेहतर रूपांतरण अनुक्रम है, या

    struct A 
    {
        operator int();
        operator double();
    } a;
    
    int i = a; // a.operator int() is better than a.operator double() and a conversion
    float f = a; // ambiguous
    

    3.3। एक प्रत्यक्ष संदर्भ बंधन में, F1 में F1 F2 द्वारा एक ही तरह का संदर्भ होता है, या नहीं

    struct A 
    {
        operator X&();  // #1
        operator X&&(); // #2
    };
    A a;
    X& lx = a;  // calls #1
    X&& rx = a; // calls #2
    

    3.4। F1 एक फंक्शन टेम्प्लेट स्पेशलाइजेशन नहीं है, लेकिन F2 है, या

    template <class T> void f(T ); // #1
    void f(int );                  // #2
    
    f(42); // calls #2, the non-template
    

    3.5। F1 और F2 दोनों फंक्शन टेम्पलेट स्पेशलाइजेशन हैं, लेकिन F1 F2 तुलना में अधिक विशिष्ट है।

    template <class T> void f(T );  // #1
    template <class T> void f(T* ); // #2
    
    int* p;
    f(p); // calls #2, more specialized
    

यहां ऑर्डर देना महत्वपूर्ण है। बेहतर रूपांतरण अनुक्रम जांच टेम्पलेट बनाम गैर-टेम्पलेट चेक से पहले होती है। यह अग्रेषण संदर्भ पर ओवरलोडिंग के साथ एक सामान्य त्रुटि की ओर जाता है:

struct A {
    A(A const& ); // #1
    
    template <class T>
    A(T&& );      // #2, not constrained
};

A a;
A b(a); // calls #2!
        // #1 is not a template but #2 resolves to
        // A(A& ), which is a less cv-qualified reference than #1
        // which makes it a better implicit conversion sequence

यदि अंत में एक भी सबसे अच्छा व्यवहार्य उम्मीदवार नहीं है, तो कॉल अस्पष्ट है:

void f(double ) { }
void f(float ) { }

f(42); // error: ambiguous

अंकगणितीय पदोन्नति और रूपांतरण

पूर्णांक प्रकार को संबंधित प्रचारित प्रकार में परिवर्तित करना, इसे किसी अन्य पूर्णांक प्रकार में परिवर्तित करने से बेहतर है।

void f(int x);
void f(short x);
signed char c = 42;
f(c); // calls f(int); promotion to int is better than conversion to short
short s = 42;
f(s); // calls f(short); exact match is better than promotion to int

float को double बढ़ावा देना, इसे किसी अन्य फ्लोटिंग पॉइंट प्रकार में परिवर्तित करने से बेहतर है।

void f(double x);
void f(long double x);
f(3.14f); // calls f(double); promotion to double is better than conversion to long double

प्रोन्नति के अलावा अन्य अंकगणित रूपांतरण एक दूसरे से बेहतर या खराब नहीं हैं।

void f(float x);
void f(long double x);
f(3.14); // ambiguous

void g(long x);
void g(long double x);
g(42); // ambiguous
g(3.14); // ambiguous

इसलिए, यह सुनिश्चित करने के लिए कि किसी मानक प्रकार के अभिन्न या फ्लोटिंग-पॉइंट तर्कों के साथ फ़ंक्शन f को कॉल करते समय कोई अस्पष्टता नहीं होगी, कुल आठ ओवरलोड की आवश्यकता होती है, ताकि प्रत्येक संभावित तर्क प्रकार के लिए, या तो एक ओवरलोड मिलान हो। प्रचारित तर्क प्रकार के साथ वास्तव में या अद्वितीय अधिभार का चयन किया जाएगा।

void f(int x);
void f(unsigned int x);
void f(long x);
void f(unsigned long x);
void f(long long x);
void f(unsigned long long x);
void f(double x);
void f(long double x);

एक वर्ग पदानुक्रम के भीतर ओवरलोडिंग

निम्नलिखित उदाहरण इस वर्ग पदानुक्रम का उपयोग करेंगे:

struct A { int m; };
struct B : A {};
struct C : B {};

व्युत्पन्न वर्ग प्रकार से बेस क्लास प्रकार में रूपांतरण उपयोगकर्ता द्वारा परिभाषित रूपांतरणों के लिए पसंद किया जाता है। यह मूल्य या संदर्भ से गुजरने के साथ-साथ पॉइंटर-टू-व्युत्पन्न को पॉइंटर-टू-बेस में परिवर्तित करते समय लागू होता है।

struct Unrelated {
    Unrelated(B b);
};
void f(A a);
void f(Unrelated u);
B b;
f(b); // calls f(A)

व्युत्पन्न वर्ग से बेस क्लास के लिए एक सूचक रूपांतरण भी void* से रूपांतरण से बेहतर है void*

void f(A* p);
void f(void* p);
B b;
f(&b); // calls f(A*)

यदि वंशानुक्रम की एक ही श्रृंखला के भीतर कई अधिभार हैं, तो सबसे अधिक व्युत्पन्न आधार वर्ग अधिभार पसंद किया जाता है। यह आभासी प्रेषण के समान सिद्धांत पर आधारित है: "सबसे विशिष्ट" कार्यान्वयन चुना गया है। हालांकि, अधिभार संकल्प हमेशा संकलन समय पर होता है और कभी भी नीचे कास्ट नहीं होगा।

void f(const A& a);
void f(const B& b);
C c;
f(c); // calls f(const B&)
B b;
A& r = b;
f(r); // calls f(const A&); the f(const B&) overload is not viable

सदस्यों के संकेत के लिए, जो वर्ग के संबंध में विरोधाभासी हैं, एक समान नियम विपरीत दिशा में लागू होता है: सबसे कम व्युत्पन्न वर्ग को प्राथमिकता दी जाती है।

void f(int B::*p);
void f(int C::*p);
int A::*p = &A::m;
f(p); // calls f(int B::*)

कब्ज और अस्थिरता पर ओवरलोडिंग

T* पैरामीटर को पॉइंटर तर्क पास करना, यदि संभव हो तो, इसे const T* पैरामीटर में पास करने से बेहतर है।

struct Base {};
struct Derived : Base {};
void f(Base* pb);
void f(const Base* pb);
void f(const Derived* pd);
void f(bool b);

Base b;
f(&b); // f(Base*) is better than f(const Base*)
Derived d;
f(&d); // f(const Derived*) is better than f(Base*) though;
       // constness is only a "tie-breaker" rule

इसी तरह, T& पैरामीटर के लिए एक तर्क पारित करना, यदि संभव हो, तो इसे एक const T& पैरामीटर से पास करने से बेहतर है, भले ही दोनों में सटीक मिलान रैंक हो।

void f(int& r);
void f(const int& r);
int x;
f(x); // both overloads match exactly, but f(int&) is still better
const int y = 42;
f(y); // only f(const int&) is viable

यह नियम कॉन्स्ट-योग्य सदस्य कार्यों के लिए भी लागू होता है, जहां गैर-कॉस्ट ऑब्जेक्ट्स के लिए म्यूटेबल एक्सेस और कॉन्स ऑब्जेक्ट्स के लिए अपरिवर्तनीय एक्सेस की अनुमति देना महत्वपूर्ण है।

class IntVector {
  public:
    // ...
    int* data() { return m_data; }
    const int* data() const { return m_data; }
  private:
    // ...
    int* m_data;
};
IntVector v1;
int* data1 = v1.data();       // Vector::data() is better than Vector::data() const;
                              // data1 can be used to modify the vector's data
const IntVector v2;
const int* data2 = v2.data(); // only Vector::data() const is viable;
                              // data2 can't be used to modify the vector's data

उसी तरह, एक गैर-वाष्पशील अधिभार की तुलना में एक अस्थिर अधिभार कम पसंद किया जाएगा।

class AtomicInt {
  public:
    // ...
    int load();
    int load() volatile;
  private:
    // ...
};
AtomicInt a1;
a1.load(); // non-volatile overload preferred; no side effect
volatile AtomicInt a2;
a2.load(); // only volatile overload is viable; side effect
static_cast<volatile AtomicInt&>(a1).load(); // force volatile semantics for a1


Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow