2016-09-19 11 views
11

कल्पना कीजिए हम निम्नलिखित स्थिति है विवश नहीं करता std::copy_if टेम्पलेट हस्ताक्षर के बाद से संकलक द्वारा विधेय किसी भी प्रकार स्वीकार करता है:क्यों std :: हस्ताक्षर copy_if विधेय प्रकार

template<typename _IIter, 
     typename _OIter, 
     typename _Predicate> 
_OIter copy_if(// etc... 

मैंने पाया है कि अगर मैं एक और अधिक constra में std::copy_if कॉल लपेट अधिभार संकल्प काम कर इनेड टेम्पलेट फ़ंक्शन:

template<typename _IIter, 
     typename _OIter, 
     typename _Predicate = bool(const typename std::iterator_traits<_IIter>::value_type&) > 
void copy_if(_IIter source_begin, 
       _IIter source_end, 
       _OIter target, 
       _Predicate pred) 
{ 
    std::copy_if(source_begin, source_end, target, pred); 
} 

मेरा प्रश्न है: क्यों एसटीएल में यह पहले से ही बाध्य नहीं है? मैंने जो देखा है, उससे _Predicate प्रकार कोई ऐसा फ़ंक्शन नहीं है जो bool देता है और पुनरावृत्त इनपुट प्रकार स्वीकार करता है, फिर भी यह एक कंपाइलर त्रुटि उत्पन्न करने जा रहा है। तो इस हस्ताक्षर को पहले ही हस्ताक्षर में क्यों न डालें, ताकि ओवरलोड रिज़ॉल्यूशन काम कर सके?

+1

आपकी बाधा बहुत मजबूत है ('const' की आवश्यकता नहीं है, कुछ रूपांतरण की अनुमति है (' int' से 'bool'))। 'decltype' सही आवश्यकता (या अवधारणा) की अनुमति देगा, लेकिन यह विधि C++ 11 से पहले की गई थी। – Jarod42

उत्तर

12

क्योंकि भविष्यवाणी को एक कार्य नहीं होना चाहिए, लेकिन यह एक मजेदार भी हो सकता है। और मज़ेदार प्रकार को सीमित करना असंभव के करीब है क्योंकि यह operator() परिभाषित होने तक कुछ भी हो सकता है।

असल में मैं तुम्हें एक बहुरूपी functor यहाँ करने के लिए अतिभारित समारोह में परिवर्तित करने का सुझाव:

struct predicate { 
    bool operator()(const A& a) const 
    { 
     return a.i > 123; 
    } 

    bool operator()(const B& b) const 
    { 
     return operator()(b.a); 
    } 
} 

और

std::copy_if(a_source.begin(), a_source.end(), std::back_inserter(a_target), predicate()); 
std::copy_if(b_source.begin(), b_source.end(), std::back_inserter(b_target), predicate()); 
//                      ^^ here, see the() 

तो सही अधिभार के अंदर का चयन किया जाएगा एक उदाहरण के साथ functor कहते हैं, यानी कलन विधि।

+0

यह बहुत दिलचस्प है। दो प्रश्न: (1) इस मामले में सही अधिभार का चयन क्यों किया जाता है? (2) क्या 'ऑपरेटर() 'स्थिर बनाना संभव है? – nyarlathotep108

+2

@ nyarlathotep108, विज्ञापन (1), टेम्पलेट अब पैकेज में दोनों अधिभार प्राप्त करता है और चयन टेम्पलेट के अंदर गहरा होता है जहां वास्तव में ऐसा करने के लिए पर्याप्त जानकारी होती है। विज्ञापन (2), हां, यह होना चाहिए, लेकिन इससे कोई फर्क नहीं पड़ता क्योंकि आपको डमी इंस्टेंस बनाने की ज़रूरत है। –

+0

और, वास्तव में, लैम्बडास को फ़ंक्शन ऑब्जेक्ट्स के संदर्भ में परिभाषित किया जाता है, और केवल गैर-कैप्चरिंग लैम्बडास में फ़ंक्शन पॉइंटर परिभाषित किया जाता है (आपको कहीं कैप्चर किए गए मानों को स्टोर करना होगा!) – JohannesD

4

यह समस्या न केवल एल्गोरिदम की भविष्यवाणी को प्रभावित करती है। यह कहीं भी होता है कि टेम्पलेट प्रकार की कटौती एक ओवरलोडेड फ़ंक्शन को घटाती है। टेम्पलेट प्रकार की कटौती ओवरलोड रिज़ॉल्यूशन से पहले होती है, इसलिए कंपाइलर अस्पष्टता को हल करने के लिए प्रासंगिक जानकारी की कमी करता है।

सही ढंग से लिखित बाधा जटिल रूप से जटिल होगी, क्योंकि इसे खाता तर्क और रिटर्न प्रकार रूपांतरण, बाइंड्स, लैम्बडास, फ़ैक्टर, mem_fn एस और अन्य में ध्यान देने की आवश्यकता होगी।

अस्पष्टता (आईएमएचओ) को हल करने का एक आसान तरीका लैम्बडा के माध्यम से भविष्यवाणी करना है।

std::copy_if(a_source.begin(), a_source.end(), 
     std::back_inserter(a_target), 
     [](auto&& x){ return predicate(std::forward<decltype(x)>(x)); }); 

यह टेम्पलेट प्रकार की कटौती के बाद तक अधिभार संकल्प को रोकता है।

क्या होगा यदि मैं मना (या मेरे मालिक मना कर दिया) C++ 14

फिर हाथ से रोल ही लैम्ब्डा को उन्नत करने के लिए:

struct predicate_caller 
{ 
    template<class T> 
    decltype(auto) operator()(T&& t) const 
    { 
    return predicate(std::forward<T>(t)); 
    } 
}; 

और इतने की तरह फोन:

std::copy_if(b_source.begin(), b_source.end(), 
      std::back_inserter(b_target), 
      predicate_caller()); 
+0

हालांकि, यह केवल सी ++ 14 जेनेरिक लैम्बडास के साथ काम करता है। जो इतना नया है कि यह आपके स्थानीय कंपाइलर पर अभी तक उपलब्ध नहीं हो सकता है। –

+0

@JanHudec मैं एक सी ++ 11 कंपाइलर के बारे में नहीं सोच सकता जिसके लिए सी ++ 14 में कोई दर्द रहित अपग्रेड नहीं है। मेरा विचार यह है कि सी ++ 11 के साथ रहना ही एक विरोधी पैटर्न है। हालांकि, टेम्पलेट कॉल ऑपरेटर के साथ एक फ़ंक्शन ऑब्जेक्ट पर्याप्त होगा। –

+0

@JanHudec हाथ से लुढ़का हुआ टेम्पलेटर मिक्सर वहां सभी मर-हार्ड के लिए प्रदान किया गया। –

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