5

C++ 11 से पहले का उचित उपयोग, मैं इस तरह कोड लिखने के लिए इस्तेमाल किया:सार्वभौमिक संदर्भ

// Small functions 
void doThingsWithA(const A& a) 
{ 
    // do stuff 
} 

void doThingsWithB(const B& b) 
{ 
    // do stuff 
} 

void doThingsWithC(const C& c) 
{ 
    // do stuff 
} 

// Big function 
void doThingsWithABC(const A& a, const B& b, const C& c) 
{ 
    // do stuff 
    doThingsWithA(a); 
    doThingsWithB(b); 
    doThingsWithC(c); 
    // do stuff 
} 

लेकिन अब, इस कदम अर्थ विज्ञान के साथ, यह दिलचस्प (कम से कम कुछ मामलों में) की अनुमति के लिए हो सकता है मेरी कार्यों पैरामीटर के रूप में rvalue संदर्भ लेने के लिए और इन भार के जोड़ने के लिए:

void doThingsWithA(A&& a); 
void doThingsWithB(B&& b); 
void doThingsWithC(C&& c); 

मैं क्या इकट्ठा होते हैं, अगर मैं अपने बड़े समारोह के भीतर उन भार के कॉल करने के लिए सक्षम होना चाहते हैं से, मैं की तरह लग रहे हो सकता है जो सही अग्रेषण का उपयोग करने की जरूरत है, यह (यह थोड़ा कम पठनीय है, लेकिन मुझे लगता है कि यह एक अच्छा नामकरण संयोजन के साथ ठीक हो सकता है टेम्पलेट प्रकार के लिए tion):

template<typename TplA, typename TplB, typename TplC> 
void doThingsWithABC(TplA&& a, TplB&& b, TplC&& c) 
{ 
    // do stuff 
    doThingsWithA(std::forward<TplA>(a)); 
    doThingsWithB(std::forward<TplB>(b)); 
    doThingsWithC(std::forward<TplC>(c)); 
    // do stuff 
} 

मेरे समस्या यह है: कि इसका मतलब यह नहीं है कि अगर मेरे छोटे कार्यों अन्य भार के है, यह है जिसके लिए वह था प्रकार के मानकों के साथ बड़ा एक कॉल करने के लिए संभव हो जाएगा इरादतन नही?

मुझे लगता है कि यह इसे रोकने के लिए काम कर सकते हैं:

template<typename TplA, typename TplB, typename TplC, 
class = typename std::enable_if<std::is_same<A, std::decay<TplA>::type>::value>::type, 
class = typename std::enable_if<std::is_same<B, std::decay<TplB>::type>::value>::type, 
class = typename std::enable_if<std::is_same<C, std::decay<TplC>::type>::value>::type> 
    doThingsWithABC(TplA&& a, TplB&& b, TplC&& c) 
{ 
    // do stuff 
    doThingsWithA(std::forward<TplA>(a)); 
    doThingsWithB(std::forward<TplB>(b)); 
    doThingsWithC(std::forward<TplC>(c)); 
    // do stuff 
} 

हालांकि मुझे यकीन है कि अगर यह बहुत प्रतिबंधक नहीं है, के रूप में मैं इसे कैसे बर्ताव का पता नहीं है अगर मैं बड़ा कार्यों कॉल करने के लिए कोशिश नहीं कर रहा हूँ उन प्रकारों के साथ जो ए, बी या सी के लिए निहित रूप से परिवर्तनीय हैं ...

लेकिन ... यहां तक ​​कि इस काम का अनुमान लगाते हुए, क्या मेरे पास वास्तव में कोई अन्य विकल्प नहीं है? (मेरा मतलब है ... आंखों पर यह आसान नहीं है)

+3

यदि आप अपनी आंखों पर आसान हैं तो आप 'static_assert' का उपयोग कर सकते हैं। –

+0

आप 'doThingsWithABC' के सभी 8 संस्करणों को उत्पन्न करने के लिए मैक्रो का उपयोग कर सकते हैं ... * बतख * – Brian

उत्तर

6

बिल्कुल सही अग्रेषण जब आप नहीं जानते कि कैसे डेटा, भस्म हो क्योंकि आप 'उपयोगकर्ता के आपूर्ति की' डेटा की एक सामान्य आवरण लिख रहे हैं जा रहा है के लिए मुख्य रूप से है।

ऊपर वर्णित एक सरल प्रक्रिया प्रणाली में, आपके द्वारा की जाने वाली 3 चीजें ठोस कार्य होंगी।

इसका मतलब है कि आपको पता चलेगा कि उन्हें डेटा का एक जंगम स्रोत होने से लाभ होगा या नहीं, और अगर उन्हें प्रतिलिपि बनाना है, और यदि चाल सस्ता है।

अगर प्रतिलिपि समझ में आता है, लेकिन चाल तेजी से होती है, और चाल सस्ता है (एक आम मामला), तो उन्हें मान मान लेना चाहिए और जब वे अपनी स्थानीय प्रतिलिपि संग्रहीत करते हैं तो उनमें से बाहर निकलें।

यह नियम तब फ़ंक्शन पर पुनरावर्ती रूप से लागू होता है जो 3 उप कार्यों को कॉल करता है।

यदि फ़ंक्शन को आगे बढ़ने से लाभ नहीं होता है, तो const& पर जाएं।

यदि प्रतिलिपि नहीं समझती है, तो रावल्यू संदर्भ (सार्वभौमिक संदर्भ नहीं) या मूल्य से लें।

मामले में जहां यह moveऔरmove करने में सक्षम होना दोनों अच्छा है में महंगा बना हुआ है आप सही अग्रेषण विचार करना चाहिए।जैसा कि ऊपर बताया गया है, यह आमतौर पर तब होता है जब आपके कोड बेस के 'उपयोगकर्ता' द्वारा सेट किए गए रैपिंग फ़ंक्शन, आमतौर पर move या तो वास्तव में वास्तव में सस्ता है, या प्रतिलिपि के रूप में महंगा है। आपको अग्रेषित होने के लिए सही अग्रेषण के लिए move दक्षता के मध्यवर्ती या अनिश्चित चरण में होना चाहिए।

कंटेनर म्यूटेटर जैसे परिपूर्ण अग्रेषण के लिए अन्य उपयोग हैं, लेकिन वे अधिक गूढ़ हैं। उदाहरण के तौर पर, मेरे backwards रेंज म्यूटेटर सी ++ 11 स्टाइल श्रेणी-आधारित for(:) लूप में एकाधिक रेंज म्यूटेटर्स को चेन करते समय संदर्भ जीवनकाल एक्सटेंशन को ठीक से काम करने के लिए आने वाली रेंज को स्टोरेज में आगे बढ़ाएगा।

जेनरेट कोड कोड ब्लूटेट, धीमी बिल्ड, लीकी कार्यान्वयन, और कोड को समझने में कठोर फॉरवर्ड फ़ॉरवर्डिंग परिणाम।

5

enable_if के बजाय static_assert एस का उपयोग करें। आईएमएचओ, यह विकल्प न केवल आंखों पर आसान है, बल्कि अधिक उपयोगकर्ता के अनुकूल है। यदि तर्क प्रकार का उल्लंघन किया जाता है तो संकलक एक स्पष्ट त्रुटि संदेश मुद्रित करेगा, जबकि enable_if समकक्ष के साथ यह कोई मेल खाने वाला फ़ंक्शन नहीं मिलेगा।

template<typename TplA, typename TplB, typename TplC> 
void doThingsWithABC(TplA&& a, TplB&& b, TplC&& c) 
{ 
    static_assert(std::is_same<A, std::decay<TplA>::type>::value, "arg1 must be of type A"); 
    static_assert(std::is_same<B, std::decay<TplB>::type>::value, "arg2 must be of type B"); 
    static_assert(std::is_same<C, std::decay<TplC>::type>::value, "arg3 must be of type C"); 
    // do stuff 
    doThingsWithA(std::forward<TplA>(a)); 
    doThingsWithB(std::forward<TplB>(b)); 
    doThingsWithC(std::forward<TplC>(c)); 
    // do stuff 
} 
+0

'static_assert' वास्तव में अधिक पठनीय है। हालांकि यह अन्य काम ओवरलोड होने पर काम नहीं करेगा। उस स्थिति में, 'enable_if' (SFINAE) एकमात्र विकल्प है। –

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