ให้ไว้ดังนี้

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>. ฉันชอบที่จะใช้ functor ของฉันซ้ำในเวอร์ชันที่โอเวอร์โหลดของisAllEqual(). แต่ฉันไม่สามารถ Foo*และshared_ptr<Foo>เป็นประเภทต่างๆ และฉันต้องรับช่วงต่อจากunary_functionนี้จึงจะสามารถใช้not1. คงจะดีกว่านี้ถ้าฉันไม่ต้องเขียน functor เดิมซ้ำ 2 ครั้ง

คำถาม:

  • มีวิธีใดบ้างในการเขียนIsEqualเพื่อให้สามารถใช้ทั้งพอยน์เตอร์แบบ raw และแบบสมาร์ท?
  • ฉันใส่กุญแจมือตัวเองโดยใช้std::not1? ฉันควรเขียนIsNotEqualแทนไหม

ข้อ จำกัด:

  1. ฉันใช้อะไรจากไลบรารีบูสต์ไม่ได้
  2. คอมไพเลอร์ของเราไม่เจ๋งพอที่จะรองรับแลมบ์ดา C++0x
ตอบ

เกี่ยวกับ:

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);
}
// --*-- 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;
    }
}

ช็อตของฉันจะเป็นแบบนี้:

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;
    }
};

คุณจะมีoperator()อินสแตนซ์ที่แตกต่างกันสำหรับทุกสิ่งที่ไม่สามารถอ้างอิง->ได้ ดังนั้นพอยน์เตอร์ดิบและพอยน์เตอร์อัจฉริยะ

คุณอาจทำบางสิ่งที่ยุ่งยากด้วยการแปลงโดยนัย:

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.

คำตอบของ Ben เป็นสิ่งเดียวที่คุณสามารถทำได้ใน c++03 แม้ว่าใน C++0x และ/หรือด้วย boost::bind คุณไม่จำเป็นต้องสืบทอดจาก unary_function สิ่งนี้ช่วยให้คุณใช้ตัวดำเนินการ templated () โดยปกติคุณสามารถหลีกเลี่ยงสิ่งเดียวกันได้ใน C++03 แต่ฉันคิดว่ามันไม่ถูกต้องในทางเทคนิคที่จะทำเช่นนั้น