"तर्क-निर्भर लुकअप" (उर्फ ADL, या "Koenig लुकअप") क्या है?


176

तर्क-निर्भर आश्रितता क्या है, इस पर कुछ अच्छी व्याख्याएँ हैं? कई लोग इसे कोएनिग लुकअप भी कहते हैं।

अधिमानतः मैं जानना चाहूंगा:

  • यह अच्छी बात क्यों है?
  • यह बुरी बात क्यों है?
  • यह कैसे काम करता है?



9
यह क्योंकि एक अच्छी बात है : अन्यथा std::cout << "Hello world";संकलन नहीं होगा
sehe

जवाबों:


223

कोएनिग लुकअप , या तर्क निर्भर लुकअप , वर्णन करता है कि सी ++ में संकलक द्वारा कैसे अयोग्य नामों को देखा जाता है।

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)अस्पष्ट होगा।

सामान्य ज्ञान:

इसे "Koenig लुकअप" क्यों कहा जाता है?

क्योंकि यह पूर्व एटी एंड टी और बेल लैब्स के शोधकर्ता और प्रोग्रामर एंड्रयू कोएनिग द्वारा तैयार किया गया था ।

आगे की पढाई:


1 कोएनिग लुकअप की परिभाषा जोसटिस की पुस्तक द सी ++ स्टैंडर्ड लाइब्रेरी: ए ट्यूटोरियल एंड रेफरेंस में परिभाषित की गई है।


11
@AlokSave: उत्तर के लिए +1, लेकिन सामान्य ज्ञान सही नहीं है। कोएनिग ने ADL का आविष्कार नहीं किया, क्योंकि वह यहाँ कबूल करता है :)
किंवदंतियों २

20
कोएनिग एल्गोरिथम की आलोचना में उदाहरण को कोनिग लुकअप का "फीचर" माना जा सकता है जितना कि "कोन"। इस तरह से std :: swap () का उपयोग करना एक सामान्य मुहावरा है: अधिक विशिष्ट संस्करण A :: swap () प्रदान नहीं किए जाने की स्थिति में 'std :: swap ()' का प्रयोग करें। यदि A :: swap () का एक विशेष संस्करण उपलब्ध है, तो हम चाहते हैं कि एक को बुलाया जाए। यह स्वैप () कॉल के लिए अधिक उदारता प्रदान करता है, क्योंकि हम कॉल को संकलित करने और काम करने के लिए भरोसा कर सकते हैं, लेकिन हम एक के होने पर उपयोग किए जाने वाले अधिक विशिष्ट संस्करण पर भी भरोसा कर सकते हैं।
एंथनी हॉल

6
@anthrond इसमें और भी बहुत कुछ है। आपके साथ std::swapवास्तव में यह करना है कि चूंकि एकमात्र विकल्प std::swapआपकी Aकक्षा के लिए टेम्पलेट फ़ंक्शन स्पष्ट विशेषज्ञता को जोड़ना होगा । फिर भी यदि आपकी Aकक्षा स्वयं एक टेम्पलेट है तो यह स्पष्ट विशेषज्ञता के बजाय आंशिक विशेषज्ञता होगी। और टेम्पलेट फ़ंक्शन के आंशिक विशेषज्ञता की अनुमति नहीं है। ओवरलोड जोड़ना std::swapएक विकल्प होगा, लेकिन स्पष्ट रूप से मना किया जाता है (आप stdनाम स्थान पर चीजें नहीं जोड़ सकते हैं )। तो ADL ही एकमात्र रास्ता है std::swap
एडम बदुरा

1
मुझे "केओनिग लुकअप का लाभ" के तहत अतिभारित ऑपरेटरों का उल्लेख देखने की उम्मीद होगी। उदाहरण के साथ std::swap()थोड़ा पीछे की ओर लगता है। मैं समस्या हो जब उम्मीद होती है std::swap(), टाइप करने के लिए अधिभार विशिष्ट बजाय चयन किया जाता है A::swap()। उदाहरण के साथ std::swap(A::MyClass&, A::MyClass&)भ्रामक लगता है। चूंकि stdएक उपयोगकर्ता प्रकार के लिए एक विशिष्ट अधिभार कभी नहीं होगा, मुझे नहीं लगता कि यह एक महान उदाहरण है।
अरविद

1
@gsamaras ... और? हम सभी देख सकते हैं कि फ़ंक्शन को कभी भी परिभाषित नहीं किया गया था। आपका त्रुटि संदेश यह साबित करता है कि यह वास्तव में काम किया है, क्योंकि यह MyNamespace::doSomethingसिर्फ नहीं के लिए देख रहा है ::doSomething
निधि मोनिका का मुकदमा

69

कोएनिग लुकअप में, यदि किसी फ़ंक्शन को उसके नाम स्थान को निर्दिष्ट किए बिना कहा जाता है, तो एक फ़ंक्शन का नाम भी नामस्थान (ओं) में खोजा जाता है जिसमें तर्क का प्रकार परिभाषित किया गया है। यही कारण है कि इसे संक्षेप में ADL के रूप में तर्क-निर्भर नाम लुकअप के रूप में भी जाना जाता है ।

यह Koenig लुकअप की वजह से है, हम इसे लिख सकते हैं:

std::cout << "Hello World!" << "\n";

अन्यथा, हमें लिखना होगा:

std::operator<<(std::operator<<(std::cout, "Hello World!"), "\n");

जो वास्तव में बहुत अधिक टाइपिंग है और कोड वास्तव में बदसूरत लगता है!

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


12
प्रेरक उदाहरण।
एंथनी हॉल

10
@ अदमबुरा: कृपया ध्यान दें कि std::coutफ़ंक्शन का एक तर्क है, जो ADL को सक्षम करने के लिए पर्याप्त है। क्या आपने ध्यान दिया?
नवाज

1
@meet: आपके प्रश्न को एक लंबे उत्तर की आवश्यकता है जो इस स्थान पर प्रदान नहीं किया जा सकता है। इसलिए मैं आपको केवल ऐसे विषयों पर पढ़ने की सलाह दे सकता हूं जैसे: 1) हस्ताक्षर ostream<<(जैसा कि यह तर्क के रूप में लेता है और यह क्या रिटर्न देता है)। 2) पूरी तरह से योग्य नाम (जैसे std::vectorया std::operator<<)। 3) तर्क पर निर्भर लुकअप का अधिक विस्तृत अध्ययन।
नवाज

2
@WorldSEnder: हां, आप सही कह रहे हैं। वह फ़ंक्शन जो std::endlतर्क के रूप में ले सकता है, वास्तव में एक सदस्य फ़ंक्शन है। वैसे भी, अगर मैं "\n"इसके बजाय उपयोग करता हूं std::endl, तो मेरा उत्तर सही है। टिप्पणी के लिए धन्यवाद।
नवाज

2
@Destructor: क्योंकि एक फ़ंक्शन कॉल के रूप में f(a,b)एक नि: शुल्क फ़ंक्शन को आमंत्रित करता है। तो इस मामले में std::operator<<(std::cout, std::endl);, ऐसा कोई मुफ्त कार्य नहीं है जो std::endlदूसरे तर्क के रूप में लेता है। यह सदस्य फ़ंक्शन है जो std::endlतर्क के रूप में लेता है, और जिसके लिए आपको लिखना होगा std::cout.operator<<(std::endl);। और चूंकि एक स्वतंत्र कार्य है जो char const*दूसरे तर्क के रूप में लेता है, "\n"काम करता है; '\n'साथ ही काम करेगा।
नवाज

30

हो सकता है कि क्यों, के साथ शुरू करना सबसे अच्छा है, और उसके बाद ही कैसे जाना है।

जब नामस्थानों को पेश किया गया था, तो नाम स्थान में परिभाषित सब कुछ था, ताकि अलग-अलग पुस्तकालय एक-दूसरे के साथ हस्तक्षेप न करें। हालाँकि, इससे ऑपरेटरों को एक समस्या हुई। निम्न कोड में उदाहरण के लिए देखें:

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()


धन्यवाद! कभी समझ में नहीं आया कि इसका क्या कारण था!
user965369

20

इसके बारे में सब कुछ अच्छा नहीं है, मेरी राय में। संकलक विक्रेताओं सहित, लोग कभी-कभी इसके दुर्भाग्यपूर्ण व्यवहार के कारण इसका अपमान करते रहे हैं।

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)।


मैंने हमेशा सोचा है कि पहली बार में तर्कों पर विचार करने से क्या लाभ है।
डेनिस ज़िकफ़ोज़ो

क्या यह कहना उचित है कि एडीएल को केवल ऑपरेटरों के लिए अनुशंसित किया जाता है और अन्य कार्यों के लिए स्पष्ट रूप से नामस्थान लिखना बेहतर होता है?
बाल्की

क्या यह तर्कों के आधार वर्गों के नामस्थानों पर भी विचार करता है? (यह पागल होगा अगर यह, निश्चित रूप से)।
एलेक्स बी

3
कैसे ठीक करना है? उपयोग std :: start?
पल्म

2
@ अंपुलम हां, std::beginनेमस्पेस अस्पष्टता को साफ करता है।
निकोस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.