2010-12-13 14 views
8

निम्नलिखित को देखते हुए:क्या मैं एक सी ++ फंक्शन लिख सकता हूं जो कच्चे सूचक और स्मार्ट पॉइंटर दोनों को स्वीकार करता है?

struct Foo 
{ 
    int bar() const; 
}; 

struct IsEqual : public std::unary_function<Foo*, bool> 
{ 
    int val; 
    IsEqual(int v) : val(v) {} 

    bool operator()(const Foo* elem) const 
    { 
     return elem->bar() == val; 
    } 
}; 

मैं Foo* के एक कंटेनर है और मैं std::find_if और std::not1 का उपयोग पता लगाने के लिए अगर वहाँ कंटेनर में किसी भी तत्व हैं जहां bar() रिटर्न कुछ एक दिया मूल्य से अलग। भविष्य में

// Are all elements equal to '2'? 
bool isAllEqual(const std::vector<Foo*> &vec) 
{ 
    return find_if(vec.begin(), vec.end(), std::not1(IsEqual(2))) == vec.end(); 
} 

फास्ट आगे और अब मैं एक अलग कंटेनर है, इस बार std::tr1::shared_ptr<Foo> युक्त कोड इस तरह दिखता है। मुझे isAllEqual() के अधिभारित संस्करण में बस अपने मज़ेदार का फिर से उपयोग करना अच्छा लगेगा। लेकिन मैं नहीं कर सकता। Foo* और shared_ptr<Foo> विभिन्न प्रकार हैं। और मुझे unary_function से उत्तराधिकारी होने की आवश्यकता है, इसलिए मैं not1 का उपयोग कर सकता हूं। अगर मैं दो बार एक ही मजेदार लिखने से बच सकता तो यह और अधिक सुरुचिपूर्ण होगा।

सवाल:

  • वहाँ किसी भी तरह से IsEqual तो यह दोनों कच्चे और स्मार्ट संकेत का उपयोग कर सकते लिखने के लिए है?
  • क्या मैंने std::not1 का उपयोग करके खुद को हाथ से पकड़ा? क्या मुझे इसके बजाय IsNotEqual लिखना चाहिए?

प्रतिबंध:

  1. मैं बढ़ावा पुस्तकालय से कुछ भी उपयोग नहीं कर सकते।
  2. हमारा कंपाइलर C++ 0x lambdas का समर्थन करने के लिए पर्याप्त ठंडा नहीं है।
+1

यह एक उदाहरण की तरह लगता है जहां टेम्पलेट्स अच्छा होगा। – GWW

+0

@ क्रिस्टो: क्या आपका कंपाइलर अन्य सी ++ 0x सामान प्रदान करने के लिए पर्याप्त ठंडा है, जैसे 'std :: start'? –

+0

@ बेन, हम जीसीसी 4.1.2 का उपयोग कर रहे हैं, तो शायद नहीं। 'std :: start' और' std :: end' लिखने के लिए तुच्छ होना चाहिए। –

उत्तर

2
// --*-- C++ --*-- 

#include <vector> 
#include <algorithm> 
#include <iostream> 

// Template unary function example. 
template <typename T> 
struct IsEqual : public std::unary_function<T, bool> 
{ 
    int v; 

    IsEqual (int v) : v (v) {} 

    bool operator() (const T & elem) const 
    { 
     return elem ? elem->bar() == v : false; 
    } 
}; 

// Generic algorithm implementation example... 
template <typename T1, typename T2> 
bool isAllEqual (const T1 & c, T2 v) 
{ 
    return find_if (
     c.begin(), c.end(), 
     std::not1 (IsEqual <typename T1::value_type> (v))) == c.end(); 
} 

// Some arbitrary pointer wrapper implementation, 
// provided just for an example, not to include any 
// specific smart pointer implementation. 
template <typename T> 
class WrappedPtr 
{ 
    const T *v; 

public: 
    typedef void (WrappedPtr<T>::*unspecified_boolean_type)() const; 

    WrappedPtr (const T *v) : v (v) {} 

    const T *operator ->() const { return v; } 

    operator unspecified_boolean_type() const 
    { 
     return v != NULL ? 
      &WrappedPtr<T>::unspecified_boolean_true : NULL; 
    } 

private: 
    void unspecified_boolean_true() const {} 
}; 

// Example of structure that could be used with our algorithm. 
struct Foo 
{ 
    int v; 

    Foo (int v) : v (v) {} 

    int bar() const 
    { 
     return v; 
    } 
}; 

// Usage examples... 
int main() 
{ 
    Foo f1 (2), f2 (2); 

    // Example of using raw pointers... 
    { 
     std::vector<Foo *> vec; 
     vec.push_back (NULL); 
     vec.push_back (&f1); 
     vec.push_back (&f2); 

     if (isAllEqual (vec, 2)) 
      std::cout << "All equal to 2" << std::endl; 
     else 
      std::cout << "Not all equal to 2" << std::endl; 
    } 

    // Example of using smart pointers... 
    { 
     std::vector< WrappedPtr<Foo> > vec; 
     vec.push_back (NULL); 
     vec.push_back (&f1); 
     vec.push_back (&f2); 

     if (isAllEqual (vec, 2)) 
      std::cout << "All equal to 2" << std::endl; 
     else 
      std::cout << "Not all equal to 2" << std::endl; 
    } 
} 
+0

+1। मुझे ठीक लगता है। –

+0

@Vlad: अच्छे पुराने साधारण सरणी के साथ काम नहीं करता है। साथ ही, क्या यह 'unary_function :: Arg' पैरामीटर प्रकार' ऑपरेटर()() 'से भिन्न होने के लिए एक अच्छा विचार है? –

+0

@ बेन: मुझे लगता है कि एक ही प्रकार के निरंतर संदर्भ के लिए यह ठीक है। सरणी का समर्थन करने के लिए, मुझे लगता है कि सरल सरणी के लिए आपको टेम्पलेट शून्य फू (कॉन्स (और सरणी) [लेन]) जैसे अलग विशेषज्ञता होना चाहिए ... या ऐसा कुछ। –

2

मेरे शॉट कुछ इस तरह होगा:

template<typename PtrToFoo> 
struct IsEqual : public std::unary_function<PtrToFoo, bool> 
{ 
    int val; 
    IsEqual(int v) : val(v) {} 

    bool operator()(PtrToFoo elem) const 
    { 
     return elem->bar() == val; 
    } 
}; 

आप सब कुछ -> साथ dereferencable, तो कच्चे संकेत और स्मार्ट संकेत के लिए एक अलग operator() इन्स्टेन्शियशन होगा।

+0

उम्म, उस बेस क्लास के बारे में ... –

+0

आप यह कर सकते हैं? मैंने सोचा कि 'unary_function' के लिए पहला टेम्पलेट तर्क' ऑपरेटर() 'के तर्क प्रकार से मेल खाना था। –

+0

नहीं, आप नहीं कर सकते। हाँ यह करता है। –

8

के बारे में कैसे:

template<typename T> 
struct IsEqual : public std::unary_function<const T&, bool> 
{ 
    int val; 
    IsEqual(int v) : val(v) {} 

    bool operator()(const T& elem) const 
    { 
     return elem->bar() == val; 
    } 
}; 

template<typename T> 
IsEqual<T> DeduceEqualityComparer(int v, T) { return IsEqual<T>(v); } 

// Are all elements equal to '2'? 
template<typename TContainer> 
bool isAllEqual(const TContainer& coll) 
{ 
    using std::begin; // in C++0x, or else write this really simple function yourself 
    using std::end; 
    if (begin(coll) == end(coll)) return true; 
    return find_if(begin(coll), end(coll), std::not1(DeduceEqualityComparer(2, *begin(coll)))) == end(coll); 
} 
+0

हमम, मुझे यह पसंद है। हालांकि, यह वास्तव में 'IsEqual > '(और तब मुझे deducer लिखना नहीं होगा) से कम टाइपिंग नहीं है। वैसे भी +1। –

+0

@ क्रिस्टो: ठीक है, लेकिन अब आ रहा है 'isAllEqual' एक टेम्पलेट भी, कोड आ रहा है। –

+0

आप 'IsEqual' को एक डिफ़ॉल्ट टेम्पलेट तर्क भी दे सकते हैं: 'टेम्पलेट स्ट्रक्चर IsEqual ...' और फिर 'IsEqual' का उपयोग जारी रखें, जितना पहले' Foo * 'के लिए। – aschepler

1

आप शायद अंतर्निहित रूपांतरण के साथ कुछ मुश्किल कर सकता है:

class IsEqualArg { 
public: 
    // Implicit conversion constructors! 
    IsEqualArg(Foo* foo) : ptr(foo) {} 
    IsEqualArg(const std::tr1::shared_ptr<Foo>& foo) : ptr(&*foo) {} 
private: 
    Foo* ptr; 
    friend struct IsEqual; 
}; 

struct IsEqualArg : public std::unary_function<IsEqualArg, bool> { 
    bool operator()(const IsEqualArg& arg) const; 
    //... 
}; 

लेकिन मैं वास्तव में नहीं बल्कि सिर्फ एक IsNotEqual लिखना चाहते हैं।

0

बेन का जवाब वास्तव में एकमात्र चीज है जिसे आप सी ++ 03 में कर सकते हैं। हालांकि C++ 0x में, और/या बूस्ट :: बाइंड के साथ, आपको unary_function से प्राप्त करने की आवश्यकता नहीं है। यह आपको एक templated() ऑपरेटर का उपयोग करने की अनुमति देता है। आप आम तौर पर सी ++ 03 में इससे दूर हो सकते हैं लेकिन मुझे लगता है कि ऐसा करने के लिए तकनीकी रूप से गलत है।

+0

TR1 'बाइंड 'ठीक हो सकता है। मुझे उन लोगों के साथ जांच करनी होगी जो हमारे कोड के साथ काम करते हैं। और एक templated 'ऑपरेटर()' लिखना गलत नहीं है। यह 'not1' का उपयोग है जो चिपकने वाला बिंदु है। 'ऑपरेटर()' में एक शून्य सूचक की जांच के लिए –