2012-11-21 14 views
5

यह this प्रश्न पर एक प्रकार का अनुवर्ती है।ओवरलोड रिज़ॉल्यूशन एक फ़ंक्शन को हल करता है जो अभी तक दिखाई नहीं दे रहा है

#include <iostream> 

struct type1 {}; 
struct type2 {}; 

void foo(type1 x) 
{ 
    std::cout << "foo(type1)" << std::endl; 
} 

template<typename T> 
void bar() { 
    foo(T()); 
} 

int main() 
{ 
    bar<type1>(); 
    bar<type2>(); 
    return 0; 
} 

void foo(type2 x) 
{ 
    std::cout << "foo(type2)" << std::endl; 
} 

ऊपर कोड foo(type2) में main में bar<type2> की इन्स्टेन्शियशन के समय में दिखाई नहीं देता है। और फिर भी कोड को संकलित करता है और निम्नलिखित उत्पादन का उत्पादन:

foo(type1) 
foo(type2) 

संकलक कैसे पता है कि foo(type2) जब main में bar<type2> instantiating उपलब्ध है?

संपादित करें: मैं टेम्पलेट तत्काल कार्य के दौरान ओवरलोड रिज़ॉल्यूशन के बारे में और अधिक समझने की कोशिश कर रहा हूं।

#include <iostream> 

struct type1 {}; 
struct type2 {}; 
struct type3 { 
    operator type2() { return type2(); } 
}; 

void foo(type1 x) 
{ 
    std::cout << "foo(type1)" << std::endl; 
} 

void foo(type2 x) 
{ 
    std::cout << "foo(type2)" << std::endl; 
} 

int main() 
{ 
    foo(type3()); 
    return 0; 
} 

void foo(type3 x) 
{ 
    std::cout << "foo(type3)" << std::endl; 
} 

उत्पादन

foo(type2) 

हालांकि एक करीब मैच foo(type3) उपलब्ध है, कॉल foo(type3())foo(type2) ले कर जाता है क्योंकि यह है कि केवल उम्मीदवार कि संकलक द्वारा पार्स किया गया है किया गया है: नीचे दिए गए कोड पर विचार करें उस बिंदु तक। अब निम्नलिखित कोड पर विचार करें:

#include <iostream> 

struct type1 {}; 
struct type2 {}; 
struct type3 { 
    operator type2() { return type2(); } 
}; 

void foo(type2 x) 
{ 
    std::cout << "foo(type2)" << std::endl; 
} 

template<typename T> 
void bar() { 
    foo(T()); 
} 

int main() 
{ 
    bar<type3>(); 
    return 0; 
} 

void foo(type3 x) 
{ 
    std::cout << "foo(type3)" << std::endl; 
} 

उत्पादन

foo(type3) 

कि कॉल bar<type3>() के बिंदु पर है, है, भले ही केवल foo(type2) दिखाई दे रहा है, संकलक अभी भी foo(type3) कि बाद में आता है क्योंकि उठाता यह एक करीबी मैच है।

उत्तर

3

उत्तर तर्क-निर्भर नाम लुकअप (एडीएल) के माध्यम से मिलता है (जो लिंक किए गए प्रश्न में भी उल्लिखित है)। foo(T()); में दो लुकअप हैं। सबसे पहले टेम्पलेट परिभाषा समय पर, परिभाषा बिंदु पर परिभाषित किसी भी कार्य को अधिभार सेट में शामिल किया गया है। इसका मतलब यह है कि जब संकलक foo(T());bar के अंदर देखता है, तो यह ओवरलोड सेट में केवलvoid foo(type1 x) जोड़ता है। हालांकि दूसरा लुकअप है जिसे एडीएल कहा जाता है। टेम्पलेट तत्काल समय पर, यानी bar<type2>(); यह उसी नामस्थान में foo जैसा तर्क प्रदान करता है, जो इस मामले में type2 है। चूंकि type2 वैश्विक नामस्थान में है, तो यह foo की तलाश करता है जो वैश्विक नामस्थान में type2 लेता है और इसे पाता है, और कॉल को हल करता है। यदि आप मानक से जानकारी की तलाश में हैं, तो 14.6.4.2 Candidate functions देखें।

निम्न का प्रयास करें और कोड विफल करें देखें। ऐसा इसलिए है क्योंकि उसी नामस्थान में a::type1 के रूप में नहीं मिल सकता है।

#include <iostream> 

namespace a 
{ 
    struct type1 {}; 
} 

template<typename T> 
void bar() { 
    foo(T()); 
} 

int main() 
{ 
    bar<a::type1>(); 
    return 0; 
} 

void foo(a::type1 x) 
{ 
    std::cout << "foo(a::type1)" << std::endl; 
} 
+0

आपका कोड जीसीसी 4.6 के साथ ठीक से संकलित करता है। – keveman

+0

@keveman: [यहां] से उद्धरण (http://gcc.gnu.org/gcc-4.7/changes.html): 'जी ++ अब दो चरण लुकअप नियमों को सही ढंग से लागू करता है जैसे टेम्पलेट में उपयोग किए गए अयोग्य नाम टेम्पलेट की परिभाषा के बिंदु पर या तत्कालता के बिंदु पर तर्क-निर्भर लुकअप द्वारा दायरे में एक उचित घोषणा है। जीसीसी 4.7 से लागू किया गया है। –

+0

ठीक है। यह जीसीसी 4.7 के साथ विफल रहता है। धन्यवाद। हालांकि, मुझे क्या रोकता है यह तथ्य है कि, मेरे उदाहरण में, 'foo (type3) 'न तो' टेम्पलेट की परिभाषा के बिंदु पर 'और' तत्कालता के बिंदु पर 'उपलब्ध नहीं है। अभी भी वह है जिसे बुलाया जाता है। – keveman

6

किसी भी परिभाषा के बिना छोड़ा गया कोई भी लिंक लिंकिंग प्रक्रिया के दौरान प्रतिस्थापित किया जाना चाहिए, क्योंकि foo(type2) फ़ंक्शन किसी अन्य फ़ाइल में प्रदान किया जा सकता था।

कंपाइलर यह कहने के लिए है कि क्या कार्य को पूरी प्रक्रिया के अंत तक परिभाषित किया गया है, जब कोई और प्रतिस्थापन लागू नहीं किया जा सकता है।

आदेश समझ को स्पष्ट करने के लिए, आप संकलित करने के लिए, कहते हैं, एक आम सी कार्यक्रम आवश्यक कदम के बारे में पता होना चाहिए:

  • पहले, आप अपने कोड पर सभी मैक्रो का विस्तार;

  • तो आपका कोड भाषा वाक्यविन्यास के अनुसार मान्य है, ताकि इसे असेंबली भाषा में परिवर्तित किया जा सके - संकलन प्रक्रिया स्वयं; इस चरण के दौरान, परिभाषा के बिना पाया गया प्रत्येक प्रतीक प्रविष्टि (symbol, definition) प्रविष्टियों के साथ एक तालिका में एनोटेट किया गया है, जो बाद में पूरा हो जाएगा, जिससे आपके प्रोग्राम को ठीक तरह से बनाया जा सकेगा;

  • अगला, असेंबली में संकलित आपके कोड को मशीन भाषा में परिवर्तित कर दिया जाएगा, यानी, ऑब्जेक्ट्स बनाए जाएंगे;

  • आखिरकार, आपको प्रतीकों की परिभाषाओं पर किसी भी निर्भरता को हल करने के लिए, पहले से ही निष्पादन योग्य वस्तुओं को लिंक करने की आवश्यकता है; यह अंतिम चरण अपरिभाषित प्रतीकों के लिए आपकी ऑब्जेक्ट्स की जांच करता है, इस प्रकार प्रोग्राम को पूरा करने, अन्य मॉड्यूल या पुस्तकालयों से परिभाषाओं को जोड़ता है।

किसी भी प्रतीक सही ढंग से अपनी परिभाषा के लिए "लिंक किए गए" नहीं कर रहा था, तो संकलक बाहर एक त्रुटि अपने कार्यक्रम में इंगित करेगा - क्लासिक undefined reference to...

आपके द्वारा पोस्ट किए गए कोड को ध्यान में रखते हुए, प्रक्रिया तब तक निष्पादित की जाएगी जब तक कि यह कंपाइलर तक पहुंच न जाए। संकलक कोड को पार करेगा, type1, type2, foo(type1 x), और bar<T>() की परिभाषा पर ध्यान दें।

struct type1 {}; 
struct type2 {}; 

जब यह मुख्य तक पहुँच था, यह bar<type1>(); के लिए कॉल मिलेगा, और foo(type1()), जो पहले से ही जाना जाता है कहेंगे, और ठीक से इस्तेमाल किया जा सकता है।

void foo(type1 x) { 
    std::cout << "foo(type1)" << std::endl; 
} 

template<typename T> 
void bar() { 
    foo(T()); 
} 

int main() { 

    bar<type1>(); 
    bar<type2>(); 
    return 0; 

} 

एक बार जब यह अगली कॉल, bar<type2>(); पर पहुंच गया था, यह foo(type2()) फोन करने की कोशिश करेंगे, लेकिन ऐसी कोई परिभाषा उपयोग के लिए उपलब्ध हो जाएगा, तो यह एक अज्ञात प्रतीक के रूप में इस कॉल से संबंधित होता है, कि बदला जाना चाहिए बाद की प्रक्रियाओं में एक परिभाषा के द्वारा।

कंपाइलर main के माध्यम से चलाता है, यह एक नई परिभाषा तक पहुंचता है, जो वास्तव में "अनुवाद तालिका" पर परिभाषा की कमी है।

void foo(type2 x) { 
    std::cout << "foo(type2)" << std::endl; 
} 

तो, अगले चरण में, संकलन अपने संबंधित परिभाषा के साथ प्रतीक को बदलने के लिए सक्षम है, और कार्यक्रम सही ढंग से संकलित करता है।

सम्मान!

+0

बारीकी से देखो, 'मुख्य' से पहले 'foo (type2)' की घोषणा भी नहीं है। तो कंपाइलर को 'बार ' तत्काल करने के बिंदु पर कैसे पता चलता है, कि 'foo (type2)' अंत में दिखाई देगा? – keveman

+0

यह वही है जो मैंने इंगित किया है: कंपाइलर द्वारा पाया गया प्रत्येक प्रतीक, जिसमें कोई उपलब्ध परिभाषा नहीं है, "अनुवाद तालिका" में सूचीबद्ध है, जो लिंक-संपादन प्रक्रिया के अंत तक पूरी होनी चाहिए। यदि ऐसी तालिका में ज्ञात मूल्य के बिना कोई कुंजी है, तो संकलक शिकायत करेगा, और क्लासिक 'अपरिभाषित संदर्भ ... 'आ जाएगा; अन्यथा, प्रतिस्थापन किया जाना है। – Rubens

+0

आपके विस्तृत उत्तर के लिए धन्यवाद, लेकिन मुझे डर है कि आप एक अप्रासंगिक स्पर्शरेखा में गए हैं।यहां वास्तविक सवाल संकलन समय अधिभार संकल्प के बारे में है और लिंक समय के बारे में नहीं है। 'बार ' के तत्कालता में दो चरण नाम लुकअप के दूसरे चरण में, सी ++ कंपाइलर स्पष्ट रूप से कुछ एडीएल कर रहा है ताकि यह निर्धारित किया जा सके कि 'foo (type2)' सही 'foo' है, भले ही यह अभी तक ' foo (type2) '। मुझे शायद कुछ और याद आ रहा है कि तत्काल कैसे होता है। – keveman

संबंधित मुद्दे