तर्क-निर्भर आश्रितता क्या है, इस पर कुछ अच्छी व्याख्याएँ हैं? कई लोग इसे कोएनिग लुकअप भी कहते हैं।
अधिमानतः मैं जानना चाहूंगा:
- यह अच्छी बात क्यों है?
- यह बुरी बात क्यों है?
- यह कैसे काम करता है?
std::cout << "Hello world";
संकलन नहीं होगा
तर्क-निर्भर आश्रितता क्या है, इस पर कुछ अच्छी व्याख्याएँ हैं? कई लोग इसे कोएनिग लुकअप भी कहते हैं।
अधिमानतः मैं जानना चाहूंगा:
std::cout << "Hello world";
संकलन नहीं होगा
जवाबों:
कोएनिग लुकअप , या तर्क निर्भर लुकअप , वर्णन करता है कि सी ++ में संकलक द्वारा कैसे अयोग्य नामों को देखा जाता है।
C ++ 11 मानक § 3.4.2 / 1 राज्य:
जब फ़ंक्शन कॉल में पोस्टफ़िक्स-एक्सप्रेशन (5.2.2) एक अयोग्य-आईडी है, तो अन्य नामस्थानों को सामान्य अयोग्य लुकअप (3.4.1) के दौरान नहीं माना जा सकता है और उन नामस्थानों में, नेमस्पेस-स्कोप फ्रेंड फंक्शन की घोषणाएं ( 11.3) अन्यथा दिखाई नहीं दे सकता है। खोज के लिए ये संशोधन तर्कों के प्रकार पर निर्भर करते हैं (और टेम्पलेट टेम्पलेट तर्क के लिए, टेम्पलेट तर्क के नाम स्थान)।
सरल शब्दों में निकोलाई Josuttis कहा गया है 1 :
यदि फ़ंक्शन के नामस्थान में एक या अधिक तर्क प्रकार परिभाषित किए गए हैं, तो आपको फ़ंक्शन के लिए नामस्थान को अर्हता प्राप्त करने की आवश्यकता नहीं है।
एक सरल कोड उदाहरण:
namespace MyNamespace
{
class MyClass {};
void doSomething(MyClass);
}
MyNamespace::MyClass obj; // global object
int main()
{
doSomething(obj); // Works Fine - MyNamespace::doSomething() is called.
}
उपरोक्त उदाहरण में न तो एक using
-declaration है और न ही एक- using
निष्क्रिय है, लेकिन फिर भी संकलक कोइनेग लुकअप लागू करके doSomething()
नाम स्थान में घोषित फ़ंक्शन के रूप में अयोग्य नाम की सही पहचान करता है ।MyNamespace
एल्गोरिदम कंपाइलर को बताता है कि वह न केवल स्थानीय दायरे को देखे, बल्कि उन नामस्थानों को भी शामिल करे जिनमें तर्क का प्रकार होता है। इस प्रकार, उपरोक्त कोड में, कंपाइलर पाता है कि ऑब्जेक्ट obj
, जो फ़ंक्शन का तर्क है doSomething()
, नामस्थान का है MyNamespace
। इसलिए, यह उस नाम स्थान पर दिखता है, जहां की घोषणा का पता लगाना है doSomething()
।
जैसा कि ऊपर सरल कोड उदाहरण दर्शाता है, Koenig लुकअप प्रोग्रामर को सुविधा और उपयोग में आसानी प्रदान करता है। कोएनिग लुकअप के बिना प्रोग्रामर पर एक ओवरहेड होगा, बार-बार पूरी तरह से योग्य नामों को निर्दिष्ट करने के लिए, या इसके बजाय, कई using
-declarations का उपयोग करें ।
कोएनिग लुकअप पर अधिक निर्भरता से अर्थ संबंधी समस्याएं हो सकती हैं, और कभी-कभी प्रोग्रामर को गार्ड से पकड़ सकती हैं।
के उदाहरण पर विचार करें std::swap
, जो दो मूल्यों को स्वैप करने के लिए एक मानक पुस्तकालय एल्गोरिथ्म है। Koenig लुकअप के साथ इस एल्गोरिथ्म का उपयोग करते समय सतर्क रहना होगा क्योंकि:
std::swap(obj1,obj2);
समान व्यवहार नहीं दिखा सकते हैं:
using std::swap;
swap(obj1, obj2);
एडीएल के साथ, किस संस्करण का swap
फ़ंक्शन कहा जाता है, इस पर दिए गए तर्कों के नाम स्थान पर निर्भर करेगा।
यदि कोई नामस्थान मौजूद है A
और यदि A::obj1
, A::obj2
और A::swap()
मौजूद है तो दूसरा उदाहरण कॉल करने के लिए होगाA::swap()
, जो उपयोगकर्ता नहीं चाहता था।
इसके अलावा, अगर किसी कारण से A::swap(A::MyClass&, A::MyClass&)
और दोनों std::swap(A::MyClass&, A::MyClass&)
को परिभाषित किया जाता है, तो पहला उदाहरण कॉल करेगा std::swap(A::MyClass&, A::MyClass&)
लेकिन दूसरा संकलन नहीं करेगा क्योंकि swap(obj1, obj2)
अस्पष्ट होगा।
क्योंकि यह पूर्व एटी एंड टी और बेल लैब्स के शोधकर्ता और प्रोग्रामर एंड्रयू कोएनिग द्वारा तैयार किया गया था ।
मानक C ++ 03/11 [basic.lookup.argdep]: 3.4.2 तर्क-निर्भर नाम लुकअप।
1 कोएनिग लुकअप की परिभाषा जोसटिस की पुस्तक द सी ++ स्टैंडर्ड लाइब्रेरी: ए ट्यूटोरियल एंड रेफरेंस में परिभाषित की गई है।
std::swap
वास्तव में यह करना है कि चूंकि एकमात्र विकल्प std::swap
आपकी A
कक्षा के लिए टेम्पलेट फ़ंक्शन स्पष्ट विशेषज्ञता को जोड़ना होगा । फिर भी यदि आपकी A
कक्षा स्वयं एक टेम्पलेट है तो यह स्पष्ट विशेषज्ञता के बजाय आंशिक विशेषज्ञता होगी। और टेम्पलेट फ़ंक्शन के आंशिक विशेषज्ञता की अनुमति नहीं है। ओवरलोड जोड़ना std::swap
एक विकल्प होगा, लेकिन स्पष्ट रूप से मना किया जाता है (आप std
नाम स्थान पर चीजें नहीं जोड़ सकते हैं )। तो ADL ही एकमात्र रास्ता है std::swap
।
std::swap()
थोड़ा पीछे की ओर लगता है। मैं समस्या हो जब उम्मीद होती है std::swap()
, टाइप करने के लिए अधिभार विशिष्ट बजाय चयन किया जाता है A::swap()
। उदाहरण के साथ std::swap(A::MyClass&, A::MyClass&)
भ्रामक लगता है। चूंकि std
एक उपयोगकर्ता प्रकार के लिए एक विशिष्ट अधिभार कभी नहीं होगा, मुझे नहीं लगता कि यह एक महान उदाहरण है।
MyNamespace::doSomething
सिर्फ नहीं के लिए देख रहा है ::doSomething
।
कोएनिग लुकअप में, यदि किसी फ़ंक्शन को उसके नाम स्थान को निर्दिष्ट किए बिना कहा जाता है, तो एक फ़ंक्शन का नाम भी नामस्थान (ओं) में खोजा जाता है जिसमें तर्क का प्रकार परिभाषित किया गया है। यही कारण है कि इसे संक्षेप में ADL के रूप में तर्क-निर्भर नाम लुकअप के रूप में भी जाना जाता है ।
यह Koenig लुकअप की वजह से है, हम इसे लिख सकते हैं:
std::cout << "Hello World!" << "\n";
अन्यथा, हमें लिखना होगा:
std::operator<<(std::operator<<(std::cout, "Hello World!"), "\n");
जो वास्तव में बहुत अधिक टाइपिंग है और कोड वास्तव में बदसूरत लगता है!
दूसरे शब्दों में, कोएनिग लुकअप की अनुपस्थिति में, यहां तक कि एक हैलो वर्ल्ड कार्यक्रम भी जटिल दिखता है।
std::cout
फ़ंक्शन का एक तर्क है, जो ADL को सक्षम करने के लिए पर्याप्त है। क्या आपने ध्यान दिया?
ostream<<
(जैसा कि यह तर्क के रूप में लेता है और यह क्या रिटर्न देता है)। 2) पूरी तरह से योग्य नाम (जैसे std::vector
या std::operator<<
)। 3) तर्क पर निर्भर लुकअप का अधिक विस्तृत अध्ययन।
std::endl
तर्क के रूप में ले सकता है, वास्तव में एक सदस्य फ़ंक्शन है। वैसे भी, अगर मैं "\n"
इसके बजाय उपयोग करता हूं std::endl
, तो मेरा उत्तर सही है। टिप्पणी के लिए धन्यवाद।
f(a,b)
एक नि: शुल्क फ़ंक्शन को आमंत्रित करता है। तो इस मामले में std::operator<<(std::cout, std::endl);
, ऐसा कोई मुफ्त कार्य नहीं है जो std::endl
दूसरे तर्क के रूप में लेता है। यह सदस्य फ़ंक्शन है जो std::endl
तर्क के रूप में लेता है, और जिसके लिए आपको लिखना होगा std::cout.operator<<(std::endl);
। और चूंकि एक स्वतंत्र कार्य है जो char const*
दूसरे तर्क के रूप में लेता है, "\n"
काम करता है; '\n'
साथ ही काम करेगा।
हो सकता है कि क्यों, के साथ शुरू करना सबसे अच्छा है, और उसके बाद ही कैसे जाना है।
जब नामस्थानों को पेश किया गया था, तो नाम स्थान में परिभाषित सब कुछ था, ताकि अलग-अलग पुस्तकालय एक-दूसरे के साथ हस्तक्षेप न करें। हालाँकि, इससे ऑपरेटरों को एक समस्या हुई। निम्न कोड में उदाहरण के लिए देखें:
namespace N
{
class X {};
void f(X);
X& operator++(X&);
}
int main()
{
// define an object of type X
N::X x;
// apply f to it
N::f(x);
// apply operator++ to it
???
}
बेशक आप लिख सकते थे N::operator++(x)
, लेकिन इससे ऑपरेटर के ओवरलोडिंग के पूरे बिंदु को हरा दिया जाएगा। इसलिए एक समाधान ढूंढना पड़ा जिसने संकलक को operator++(X&)
इस तथ्य के बावजूद खोजने की अनुमति दी कि यह दायरे में नहीं था। दूसरी ओर, इसे अभी भी दूसरे में operator++
परिभाषित नहीं होना चाहिए , असंबंधित नामस्थान जो कॉल को अस्पष्ट बना सकता है (इस सरल उदाहरण में, आपको अस्पष्टता नहीं मिलेगी, लेकिन अधिक जटिल उदाहरणों में, आप हो सकते हैं)। समाधान तर्क निर्भर लुकअप (ADL) था, इस तरह से बुलाया क्योंकि लुकअप तर्क पर निर्भर करता है (अधिक सटीक रूप से, तर्क के प्रकार पर)। चूंकि इस योजना का आविष्कार एंड्रयू आर कोएनिग द्वारा किया गया था, इसलिए इसे अक्सर कोएनिग लुकअप भी कहा जाता है।
चाल यह है कि फ़ंक्शन कॉल के लिए, सामान्य नाम लुकअप के अलावा (जो उपयोग के बिंदु पर दायरे में नाम पाता है), फ़ंक्शन को दिए गए किसी भी तर्क के प्रकार के स्कोप में एक दूसरा लुकअप किया जाता है। इसलिए उपरोक्त उदाहरण में, यदि आप लिखना x++
मुख्य में, इसके लिए लग रहा है operator++
न केवल वैश्विक दायरे में है, लेकिन इसके साथ ही गुंजाइश जहां के प्रकार में x
, N::X
, परिभाषित किया गया था, यानी में namespace N
। और वहाँ यह एक मेल खाता है operator++
, और इसलिए x++
सिर्फ काम करता है। एक operator++
अन्य नामस्थान में परिभाषित एक और N2
, हालांकि, नहीं मिलेगा। चूंकि ADL नामस्थान तक सीमित नहीं है, आप भी उपयोग कर सकते हैं f(x)
के बजाय N::f(x)
में main()
।
इसके बारे में सब कुछ अच्छा नहीं है, मेरी राय में। संकलक विक्रेताओं सहित, लोग कभी-कभी इसके दुर्भाग्यपूर्ण व्यवहार के कारण इसका अपमान करते रहे हैं।
ADL C ++ 11 में फ़ॉर-रेंज लूप के एक प्रमुख ओवरहाल के लिए जिम्मेदार है। यह समझने के लिए कि एडीएल कभी-कभी अनपेक्षित प्रभाव क्यों डाल सकता है, इस बात पर विचार करें कि न केवल उन नामस्थानों को जहां तर्कों को परिभाषित किया गया है, बल्कि उन तर्कों के पैरामीटर प्रकार / सूचक प्रकारों के तर्कों के तर्कों के तर्कों के तर्क भी दिए गए हैं। , और इसी तरह आगे और पीछे।
बढ़ावा देने का उपयोग कर एक उदाहरण
std::vector<boost::shared_ptr<int>> v;
auto x = begin(v);
यह एक अस्पष्टता के परिणामस्वरूप उपयोगकर्ता, boost.range लाइब्रेरी का उपयोग करता है, तो है क्योंकि दोनों std::begin
(ADL का उपयोग करके पाया जाता है std::vector
) और boost::begin
(ADL का उपयोग करके पाया जाता है boost::shared_ptr
)।
std::begin
नेमस्पेस अस्पष्टता को साफ करता है।