2012-01-05 10 views
6

मेरे पास एक ऐसा फ़ंक्शन है जिसे कई बार एक इटरेटर की गणना करने की आवश्यकता होती है, लेकिन according to MSDN, "एक बार जब आप इनपुट इटरेटर की किसी भी प्रतिलिपि को बढ़ाते हैं, तो दूसरी प्रतियों की सुरक्षित रूप से तुलना नहीं की जा सकती है, इसके बाद संदर्भित किया जा सकता है या उसके बाद वृद्धि नहीं की जा सकती है।"एक पुनरावर्तक को अग्रेषित करने के लिए एक पुनरावर्तक को कैसे प्रतिबंधित करें?

इसलिए गैर-फ़ॉरवर्ड-इटरेटर्स के लिए एक अलग कार्यान्वयन बनाने के बजाय चीजों को आसान बनाने के लिए, प्रतिलिपि बनाने के लिए, प्रतिलिपि बनाने के लिए, मैं अपनी विधि को केवल अग्रेषित करने वालों को आगे बढ़ाने के लिए प्रतिबंधित करना चाहता हूं, और इनपुट इटरेटर्स को स्थिर रूप से अस्वीकार करना चाहता हूं ।

अभी मैं कुछ इस तरह है:

template<typename It, typename TCallback /*signature: bool(value_type)*/> 
bool EnumerateTwice(const It &begin, const It &end, TCallback callback) 
{ 
    for (It it = begin; it != end; ++it) 
     if (!callback(*it)) 
      return false; 
    for (It it = begin; it != end; ++it) 
     if (!callback(*it)) 
      return false; 
    return true; 
} 

लेकिन कुछ भी एक आगे इटरेटर होने के It प्रतिबंधित करता है।

मैं टेम्पलेट फ़ंक्शन पर उस प्रतिबंध को कैसे रखूं? (सी ++ 03)

उत्तर

5

आप SFINAE का उपयोग करें और से bool की जगह ले सकता:

typename enable_if< 
    is_same<typename std::iterator_traits<It>::iterator_category, 
      std::forward_iterator_tag>::value, 
    bool>::type 

आप अपने आप को is_same और enable_if परिभाषित करने के लिए आवश्यकता हो सकती है अगर आप उन्हें बूस्ट या TR1 से खींचने के लिए नहीं करना चाहती:

template <typename A, typename B> 
struct is_same { static const bool value = false; }; 

template <typename T> 
struct is_same<T, T> { static const bool value = true; }; 

template <bool, typename> struct enable_if { }; 
template <typename T> struct enable_if<true, T> { typedef T type; }; 
+0

+1 Oooooo नहीं जानता था कि इटरेटर टैग किए गए हैं! यह बहुत आसान है। आपका बहुत बहुत धन्यवाद! :) – Mehrdad

+9

यह बिडरेक्शनल और यादृच्छिक-पहुंच इटरेटर्स को अस्वीकार कर देगा। उचित अर्थशास्त्र के लिए 'is_same' के बजाय' is_base_of' का उपयोग करें। – ildjarn

+0

@ildjarn: ओह हाँ, अच्छा बिंदु; धन्यवाद! – Mehrdad

4

जांची नहीं है, लेकिन आप की तर्ज पर कुछ की कोशिश कर सकते:

template<typename It, typename TCallback /*signature: bool(value_type)*/> 
bool EnumerateTwice_Interal(const It &begin, const It &end, TCallback callback, 
     std::forward_iterator_tag) 
{ 
    //do your things 
} 

template<typename It, typename TCallback /*signature: bool(value_type)*/> 
bool EnumerateTwice(const It &begin, const It &end, TCallback callback) 
{ 
    EnumerateTwice_Internal(begin, end, callback, 
     typename std::iterator_traits<It>::iterator_category()); 
} 
+1

+1 SFINAE से बचने के लिए - यह क्लीनर आईएमओ है। – ildjarn

3

आप std::enable_if का उपयोग कर ऐसा कर सकते हैं:

#include <iterator> 
#include <type_traits> 
#include <utility> 

template <typename It, typename TCallback> 
typename std::enable_if<std::is_base_of<std::forward_iterator_tag, 
         typename std::iterator_traits<It>::iterator_category>::value, 
        bool>::type 
EnumerateTwice(It begin, It end, TCallback) { 
    ... 
} 

इस से सी ++ 11 वर्ग का उपयोग करता है, लेकिन इस सब के रूप में अच्छी तरह सी ++ 03 में किया जा सकता।

0

रोड्रिगो के जवाब पर विस्तार करने के लिए - मैं इस समाधान मिल गया, और सोचा था कि यह उल्लेख के लायक है:

struct True { unsigned char _[2]; operator bool() const { return true; } }; 
char is_forward_iterator(std::input_iterator_tag const *) { return 0; } 
True is_forward_iterator(std::forward_iterator_tag const *) { return True(); } 

अब, आप कुछ समारोह के अंदर यह जाँच करना चाहते हैं, तो आप कह सकते हैं:

if (is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0))) 
{ 
    ... 
} 

और आप टेम्पलेट्स के भीतर यह जाँच करना चाहते हैं, तो आप के लिए जाँच कर सकते हैं: प्राथमिक साथ

sizeof(
    is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0)) 
) > 1 

इस विधि का लाभ यह है कि यह टेम्पलेट्स घोषित करने से बचाता है (उदा। बेहतर संकलन गति के लिए)।

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