2015-03-05 21 views
7

मैं एक वर्ग विधि है कि टेम्पलेट पैरामीटर पैक लेता है लिखना चाहते हैं, लेकिन शून्य तर्क, और प्रकार पर "पुनरावृति":रिकर्सिव variadic समारोह टेम्पलेट

struct Bar { 
    template <typename T, typename... Ts> 
    void foo() { 
     // something with T that involves Bar's members 
     foo<Ts...>(); 
    } 
}; 

इस लागू करने के लिए पसंदीदा तरीका क्या है?

उत्तर

3

आप निम्न का उपयोग कर सकते हैं:

:

struct Bar { 
    template <typename... Ts> 
    void foo() { 
     int dummy[] = {0 /*Manage case where Ts is empty*/, 
         (bar<Ts>(), void() /* To avoid overload `operator,` */, 0)...}; 
     (void) dummy; // suppress warning for unused variable. 
    } 

    template <typename T> 
    void bar() 
    { 
     // something with T that involves Bar's members 
    } 

}; 

सी ++ 17 में, यह फोल्डिंग अभिव्यक्ति के साथ सरल किया जा सकता

struct Bar { 
    template <typename... Ts> 
    void foo() { 
     (static_cast<void>(bar<Ts>()), ...); 
    } 

    template <typename T> 
    void bar() 
    { 
     // something with T that involves Bar's members 
    } 

}; 
0

मैं अतिभारित कार्य करता है और एक typelist का उपयोग कर की तरह है:

#include <iostream> 
#include <typeinfo> 

template <typename ...Ts> struct typelist { }; 

void foo_impl(typelist<>) 
{ 
    // we are finished 
} 

template <typename T, typename ...Ts> 
void foo_impl(typelist<T, Ts...>) 
{ 
    std::cout << typeid(T).name() << ", "; 
    foo_impl(typelist<Ts...>{}); 
} 



template <typename ...Ts> 
void foo() 
{ 
    std::cout << "called with <"; 
    foo_impl(typelist<Ts...>{}); 
    std::cout << ">" << std::endl; 
} 


int main() 
{ 
    foo<int, char, float>(); 
} 
+0

हुह? 'foo' कोई तर्क नहीं लेता है। – MadScientist

+0

नकारात्मक पक्ष के रूप में, इसे एन प्रकारों के लिए एन रिकर्सिव चरणों की आवश्यकता होती है, और उन प्रकारों और कार्यों को तत्काल करता है जिनके नाम की लंबाई ओ (एन^2) के बराबर होती है। एन के साथ उपरोक्त बड़े कारणों से धीमी संकलन धीमी होती है, और यदि उन नामों को संकलक द्वारा नहीं हटाया जाता है तो बाइनरी ब्लोट का कारण बन सकता है। – Yakk

3
template<class...Fs> 
void do_in_order(Fs&&...fs) { 
    int _[]={0, (std::forward<Fs>(fs)(), void(), 0)...}; 
    (void)_; 
} 

छुपाता है सिंटैक्स को बाएं से दाएं क्रम में फ़ंक्शन ऑब्जेक्ट्स के पैक को निष्पादित करने के लिए आवश्यक है।

फिर

:

struct Bar { 
    template <class... Ts> 
    void foo() { 
    do_in_order([&]{ 
     using T = Ts; 
     // code 
    }...); 
    } 
}; 

और एक अनुरूप संकलक में, हम // codeT बाएं से दाएं प्रत्येक प्रकार के होने के साथ चलेंगे।

ध्यान दें कि सी ++ 11 कंपाइलर होने का दावा करने वाले कुछ कंपाइलर्स उपरोक्त संकलित करने में विफल हो सकते हैं।

इस तकनीक का लाभ यह है कि यह एक स्पष्ट नाम के साथ एक समारोह के भीतर "विस्तार और मूल्यांकन टेम्पलेट्स" कोड को छुपाता है। आप एक बार do_in_order लिखते हैं, और यह आमतौर पर उस सरणी-विस्तार चाल के लगभग हर उपयोग के लिए पर्याप्त होता है।

"अधिक सरल" पुनरावर्ती समाधानों के बजाय इस तरह के गूढ़ वाक्यविन्यास का उपयोग करने के दो महत्वपूर्ण कारण हैं।

सबसे पहले, यह अनुकूलक के लिए चीजों को आसान बनाता है। कभी-कभी ऑप्टिमाइज़र रिकर्सिव कॉल के ढेर के बाद छोड़ देते हैं।

दूसरा, परंपरागत रिकर्सिव कार्यों के लिए फ़ंक्शन हस्ताक्षर की लंबाई के नामों की योग ओ (एन^2) में बढ़ती है। यदि आप सहायक प्रकार का उपयोग करते हैं, तो नामों की कुल लंबाई भी ओ (एन^2) है। जब तक आप सावधान न हों, यह संकलन समय, लिंक समय और बाइनरी आकार के ब्लोट का कारण बन सकता है।

सी ++ 1z में कुछ "गुना" वाक्यविन्यास के लिए योजनाएं हैं जो उपर्युक्त कम गूढ़ के गूढ़ भागों को बना सकती हैं।

+0

यह बहुत साफ है। जाहिर है, जीसीसी 4.9.2 उन "कंप्यूटर्स में से एक है जो सी ++ 11 कंपाइलर्स होने का दावा करते हैं" जो इसे संकलित करने में विफल रहता है। – Barry

+0

@ बारी आइप। Clang मुझे लगता है काम करता है। किसी कारण से, जीसीसी सोचता है कि एक बयान के अंत में एक अप्रत्याशित पैरामीटर पैक एक त्रुटि है। मूर्खतापूर्ण जीसीसी: बयान विस्तार पैटर्न का केवल एक हिस्सा हो सकता है। – Yakk

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