2012-01-06 18 views
9

मैं "Printing 1 to 1000 without loop or conditionals" के लिए प्रतिक्रियाओं पढ़ रहा था और मैं सोच रहा हूँ क्यों यह NumberGeneration < 1> शीर्ष जवाब में के लिए विशेष परिस्थिति में करने के लिए आवश्यक है।संकलन समय प्रत्यावर्तन और सशर्त,

यदि मैं इसे हटा देता हूं और टेम्पलेट (नीचे कोड) में एन == 1 के लिए एक चेक जोड़ता हूं, तो कोड "टेम्पलेट इंस्टेंटेशन गहराई अधिकतम से अधिक" के साथ संकलन में विफल रहता है लेकिन मुझे यकीन नहीं है कि क्यों। संकलित समय संकलित समय में अलग-अलग हैं?

#include <iostream> 

template<int N> 
struct NumberGeneration 
{ 
    static void out(std::ostream& os) 
    { 
     if (N == 1) 
     { 
      os << 1 << std::endl; 
     } 
     else 
     { 
      NumberGeneration<N-1>::out(os); 
      os << N << std::endl; 
     } 
    } 
}; 

int main() 
{ 
    NumberGeneration<1000>::out(std::cout); 
} 

उत्तर

12

कोड जनरेशन और संकलन सशर्त के आधार पर शाखा नहीं है! इस पर विचार करें:

// don't declare bar()! 

void foo() 
{ 
    if (false) { bar(); } 
} 

तुम कभी नहीं bar() घोषित, तो यह एक संकलन त्रुटि भले ही आंतरिक गुंजाइश पर पहुंच गया कभी नहीं किया जा सकता है। इसी कारण से, NumberGeneration<N-1> हमेशा तत्काल होता है, इससे कोई फर्क नहीं पड़ता कि उस शाखा को पहुंचाया जा सकता है या नहीं, और आपके पास अनंत रिकर्सन है।

template <> struct NumberGeneration<0> { /* no more recursion here */ }; 
+0

धन्यवाद, यह समझ में आता है, मैं वास्तव में संकलन-समय तत्कालता के बारे में सोच नहीं रहा था और था यह उम्मीद है कि कोड पहुंचने पर ही किया जाएगा। –

+2

@ baris.m: लेकिन "कोड पहुंचने" के द्वारा आपका क्या मतलब है इसका एक महत्वपूर्ण सूक्ष्मता है: यह संकलक द्वारा हमेशा संकलन के दौरान, और फिर फिर * सशर्त रूप से * प्रोग्राम निष्पादन के दौरान रनटाइम पर पहुंच जाता है। –

+0

मेरी टिप्पणी में मैं रनटाइम पर पहुंचने के बारे में बात कर रहा था। आपका जवाब सही समझ में आता है। –

0

यह क्योंकि पूर्णांकों नकारात्मक हो सकता और रनटाइम कोड (if जांच) 0 के साथ टेम्पलेट, -1, -2, आदि एक संकलक प्राप्त करने में सक्षम हो सकता है instantiating संकलक बंद नहीं होगा है जो भी आप प्रस्तावित करते हैं उससे दूर, लेकिन यदि अन्य टेम्पलेट्स (0, -1, ...) को तत्काल करने पर साइड इफेक्ट्स हैं तो आप किस पर निर्भर करते हैं? संकलक उस मामले में आपके लिए तत्काल विफल नहीं हो सकता है।

संक्षेप में, सभी रिकर्सन के रूप में आपको अपना खुद का बेस केस प्रदान करना होगा।

1

मैं सोच रहा हूँ क्यों यह NumberGeneration < 1> शीर्ष जवाब में के लिए विशेष परिस्थिति में करने के लिए आवश्यक है।

क्योंकि यह रिकर्सिव के लिए अंतिम स्थिति है! इसके बिना रिकर्सिव अंत कैसे हो सकता है?

+0

यदि एन == 1 सशर्त मैं टेम्पलेट में जोड़ा गया तो क्या होगा? उस स्थिति में, हम एन-1 के साथ एक नया प्रारंभ नहीं करते हैं, इसलिए मैं रिकर्सन को रोकने की उम्मीद करता हूं। –

+0

@ baris.m: आपको स्थिति 1 के लिए टेम्पलेट विशेषज्ञता को नहीं हटाया जाना चाहिए क्योंकि संकलन के लिए कैल्काशन/संकलन समाप्त करने के लिए यह अंतिम स्थिति है। – Gob00st

+1

@ baris.m: आपके द्वारा जो कंडशन जोड़ा गया है वह संकलन के रूप में मदद नहीं कर रहा है इसके बारे में परवाह नहीं करेगा और संकलन समय के दौरान संकलन समाप्त होगा। आपके द्वारा जो चेक जोड़ा गया वह केवल रन टाइम के दौरान उपयोगी है, समय संकलित नहीं करता है। – Gob00st

4

सशर्त if संकलन समय पर संभाला नहीं किया जाएगा:

दरअसल, सशर्त, के स्थिर अनुरूप ठीक टेम्पलेट विशेषज्ञता है। इसे रनटाइम पर संभाला जाएगा।

इस प्रकार, एन = 1 के लिए भी, संकलक संख्या जनरेटर < 0> उत्पन्न करता है, फिर संख्या जनरेटर < -1> ... अंतहीन रूप से, टेम्पलेट तत्काल गहराई तक पहुंचने तक।

1

सामान्य रूप से आपके कोड में N == 1 की स्थिति रन टाइम पर मूल्यांकन की जाती है (हालांकि कंपाइलर इसे अनुकूलित कर सकता है), संकलन समय पर नहीं। इसलिए else खंड में टेम्पलेट त्वरण रिकर्सन कभी समाप्त नहीं होता है। दूसरी तरफ NumberGeneration<1> का संकलन समय पर मूल्यांकन किया जाता है और इसलिए इस पुनरावर्ती टेम्पलेट के समाप्ति मामले के रूप में कार्य करता है।

1

मुझे यकीन है कि यह संकलक-विशिष्ट है; कुछ कंपाइलर if/else की दोनों शाखाएं उत्पन्न करने का प्रयास कर सकते हैं जो भी N का मान, किसी भी घटना में संकलन विफल हो जाएगा। अन्य कंपाइलर संकलन समय पर स्थिति का मूल्यांकन कर सकते हैं, और केवल उस शाखा के लिए कोड उत्पन्न कर सकते हैं जिसे निष्पादित किया गया है, जिस स्थिति में संकलन सफल होगा।

अद्यतन: या जैसा कि ल्यूक टिप्पणियों में कहता है, यह हो सकता है कि संकलक को दोनों शाखाएं उत्पन्न करनी हों, ताकि कोड हमेशा असफल रहे। मुझे पूरा यकीन नहीं है कि मामला क्या है, लेकिन किसी भी तरह से संकलन-समय कोड जनरेशन को नियंत्रित करने के लिए रन-टाइम स्थितियों पर भरोसा करना एक बुरा विचार है।

यह विशेषज्ञता का उपयोग करना बेहतर होगा:

template <int N> 
struct NumberGeneration 
{ 
    static void out(std::ostream & os) 
    { 
     NumberGeneration<N-1>::out(os); 
     os << N << std::endl; 
    } 
}; 

template <> 
void NumberGeneration<1>::out(std::ostream & os) 
{ 
    os << 1 << std::endl; 
} 

(या आप इस थोड़ा बजाय N=0 के लिए विशेषज्ञता, एक out समारोह है कि कुछ नहीं करता है के साथ द्वारा छोटा सकता है)।

साथ ही, ध्यान रखें कि कुछ कंपाइलर गहराई से पुनर्विक्रेता टेम्पलेट का समर्थन नहीं कर सकते हैं; सी ++ 03 केवल 17 की न्यूनतम समर्थित गहराई का सुझाव देता है, जो सी ++ 11 1024 हो जाता है। आपको यह जांचना चाहिए कि आपके कंपाइलर की सीमा क्या है।

+0

हम, मुझे लगता है कि कंपाइलर * को * टेम्पलेट को तुरंत चालू करना चाहिए, भले ही यह पता लगा सके कि कोड रनटाइम पर कभी नहीं पहुंचाया जाएगा, इसलिए मुझे नहीं लगता कि यह कोड कभी भी संकलित करने वाला हो सकता है, जो भी आप संकलक करते हैं। –

+2

आपको पूरी संरचना का विशेषज्ञ करने की आवश्यकता नहीं है, आप इसे केवल फ़ंक्शन के लिए कर सकते हैं। –

+0

@ पॉलमंटा: वास्तव में आप कर सकते हैं। धन्यवाद। –

3

टेम्पलेट संकलन समय पर तत्काल होते हैं, टेम्पलेट विशेष केस कंपाइलर को संकलित करते समय 1 से नीचे रिकर्स करने से रोकता है।

अगर-क्लॉज का मूल्यांकन रनटाइम पर किया जाता है, तो संकलक पहले से ही आपके कोड को संकलित करने में विफल रहा है जब इसका कोई असर होगा।

0

यहाँ यह करने के लिए सही तरीका है:

template<int N> 
struct NumberGeneration 
{ 
    static void out(std::ostream& os); 
}; 

template<int N> 
void NumberGeneration<N>::out(std::ostream& os) 
{ 
    NumberGeneration<N-1>::out(os); 
    os << N << std::endl; 
} 

template<> 
void NumberGeneration<1>::out(std::ostream& os) 
{ 
    os << 1 << std::endl; 
} 

int main() 
{ 
    NumberGeneration<20>::out(std::cout); 
} 

टेम्पलेट विशेषज्ञता कहा जाता है यही कारण है कि: आप, प्रोग्रामर, एक वैकल्पिक परिभाषा एक टेम्पलेट की एक विशेष इन्स्टेन्शियशन के लिए प्रदान करते हैं। आप पूरे टेम्पलेट, या केवल इसके एक हिस्से का विशेषज्ञ कर सकते हैं, जैसा कि मैंने यहां किया था (मैं केवल फ़ंक्शन को विशिष्ट करता हूं, न कि संपूर्ण संरचना)।

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