लिखने के कई तरीके हैं swap, कुछ दूसरों की तुलना में बेहतर हैं। समय के साथ, हालांकि, यह पाया गया कि एकल परिभाषा सबसे अच्छा काम करती है। आइए विचार करें कि एक swapफ़ंक्शन लिखने के बारे में हम कैसे सोच सकते हैं ।
हम पहले देखते हैं कि कंटेनरों में std::vector<>एकल-तर्क सदस्य फ़ंक्शन होता है swap, जैसे:
struct vector
{
void swap(vector&) { /* swap members */ }
};
स्वाभाविक रूप से, तब, हमारी कक्षा भी सही होनी चाहिए? असल में ऐसा नहीं है। मानक पुस्तकालय में सभी प्रकार की अनावश्यक चीजें हैं , और एक सदस्य swapउनमें से एक है। क्यों? चलो आगे बढ़ें।
हमें क्या करना चाहिए, यह पहचानना है कि विहित क्या है, और इसके साथ काम करने के लिए हमारी कक्षा को क्या करने की आवश्यकता है। और स्वैपिंग की विहित विधि के साथ है std::swap। यही कारण है कि सदस्य फ़ंक्शन उपयोगी नहीं हैं: वे नहीं हैं कि हमें सामान्य रूप से चीजों को कैसे स्वैप करना चाहिए, और उनके व्यवहार पर कोई असर नहीं पड़ता है std::swap।
ठीक है, तो std::swapकाम करने के लिए हमें (और प्रदान std::vector<>करना चाहिए था) का एक विशेषज्ञता std::swap, सही?
namespace std
{
template <> // important! specialization in std is OK, overloading is UB
void swap(myclass&, myclass&)
{
// swap
}
}
ठीक है कि निश्चित रूप से इस मामले में काम करेंगे, लेकिन इसकी एक विकराल समस्या है: कार्य विशेषज्ञता आंशिक नहीं हो सकती। यही है, हम इसके साथ खाका कक्षाओं को विशेषीकृत नहीं कर सकते हैं, केवल विशेष इंस्टेंटिएशन:
namespace std
{
template <typename T>
void swap<T>(myclass<T>&, myclass<T>&) // error! no partial specialization
{
// swap
}
}
यह विधि कुछ समय के लिए काम करती है, लेकिन हर समय नहीं। इसके लिए अवश्य ही एक बेहतर तरीका होना चाहिए। '
वहाँ है! हम एक friendफ़ंक्शन का उपयोग कर सकते हैं , और इसे ADL के माध्यम से पा सकते हैं :
namespace xyz
{
struct myclass
{
friend void swap(myclass&, myclass&);
};
}
जब हम कुछ स्वैप करना चाहते हैं, तो हम want को जोड़ते हैं std::swap और फिर एक अयोग्य कॉल करते हैं:
using std::swap; // allow use of std::swap...
swap(x, y); // ...but select overloads, first
// that is, if swap(x, y) finds a better match, via ADL, it
// will use that instead; otherwise it falls back to std::swap
एक friendकार्य क्या है ? इस क्षेत्र को लेकर चारों ओर भ्रम की स्थिति है।
इससे पहले कि सी ++ मानकीकरण किया गया था, friendकार्यों कुछ 'मित्र नाम इंजेक्शन "कहा जाता है, जहां कोड से व्यवहार किया था के रूप में अगर अगर समारोह आसपास के नाम स्थान में लिखा गया था। उदाहरण के लिए, ये समान मानक थे:
struct foo
{
friend void bar()
{
// baz
}
};
// turned into, pre-standard:
struct foo
{
friend void bar();
};
void bar()
{
// baz
}
हालाँकि, जब ADL का आविष्कार किया गया था तो इसे हटा दिया गया था। friendसमारोह तो हो सकता है केवल ADL के माध्यम से पाया जा; यदि आप इसे एक नि: शुल्क फ़ंक्शन के रूप में चाहते हैं, तो इसे घोषित करने की आवश्यकता है ( उदाहरण के लिए इसे देखें )। लेकिन लो! वहाँ एक समस्या थी।
यदि आप सिर्फ उपयोग करते हैं std::swap(x, y), तो आपका अधिभार कभी नहीं मिलेगा , क्योंकि आपने स्पष्ट रूप से कहा है "देखो std, और कहीं नहीं"! यही कारण है कि कुछ लोगों ने दो कार्यों को लिखने का सुझाव दिया: एक एडीएल के माध्यम से पाया जाने वाला एक फ़ंक्शन के रूप में , और दूसरा स्पष्ट std::योग्यता को संभालने के लिए ।
लेकिन जैसा हमने देखा, यह सभी मामलों में काम नहीं कर सकता है, और हम एक बदसूरत गड़बड़ के साथ समाप्त होते हैं। इसके बजाय, मुहावरेदार अदला-बदली दूसरे मार्ग पर चली गई: यह प्रदान करने के लिए कक्षाओं की नौकरी करने के बजाय std::swap, यह सुनिश्चित करने के लिए कि यह swapउपर्युक्त की तरह योग्य उपयोग नहीं करते हैं , यह स्वैपर्स का काम है । और यह बहुत अच्छा काम करता है, जब तक लोग इसके बारे में जानते हैं। लेकिन इसमें समस्या है: यह अयोग्य है कि अयोग्य कॉल का उपयोग करने की आवश्यकता है!
इसे आसान बनाने के लिए, बूस्ट जैसे कुछ पुस्तकालयों ने फ़ंक्शन प्रदान किया boost::swap, जो कि एक संबद्ध नाम के swapसाथ सिर्फ एक अयोग्य कॉल करता है std::swap। यह चीजों को फिर से सफल बनाने में मदद करता है, लेकिन यह अभी भी एक बुमेर है।
ध्यान दें कि C ++ 11 के व्यवहार में कोई परिवर्तन नहीं है std::swap, जो कि मैंने और दूसरों ने गलती से सोचा था कि मामला होगा। यदि आप इससे थोड़ा परेशान थे, तो यहां पढ़ें ।
संक्षेप में: सदस्य फ़ंक्शन सिर्फ शोर है, विशेषज्ञता बदसूरत और अपूर्ण है, लेकिन friendफ़ंक्शन पूर्ण है और काम करता है। और जब आप स्वैप करते हैं, तो संबंधित के साथ boost::swapया तो उपयोग करें या एक अयोग्य ।swapstd::swap
† अनौपचारिक रूप से, एक नाम जुड़ा हुआ है अगर यह एक फ़ंक्शन कॉल के दौरान माना जाएगा। विवरण के लिए, §3.4.2 पढ़ें। इस मामले में, std::swapआम तौर पर विचार नहीं किया जाता है; लेकिन हम इसे जोड़ सकते हैं (इसे अयोग्य द्वारा माना गया ओवरलोड के सेट में जोड़ सकते हैं swap), जिससे इसे पाया जा सके।