2010-12-13 7 views
8

は、次のことを考える:生ポインタとスマートポインタの両方を受け付けるC++ファンクタを書くことはできますか?

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*のコンテナを持っていると私はbar()が与えられた値と異なる何かを返すコンテナ内のすべての要素があるかどうかを確認するためにstd::find_ifstd::not1を使用しています。今回はstd::tr1::shared_ptr<Foo>を含む、将来へ

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

早送りと私は今、別のコンテナを持っています。コードは次のようになります。 isAllEqual()のオーバーロードされたバージョンで、私のファンクターを単に再利用したいのです。しかし、私はできません。 Foo*shared_ptr<Foo>は異なるタイプです。そしてunary_functionから継承する必要がありますので、not1を使用できます。同じファンクタを2回書くのをやめれば、よりエレガントになります。

質問:

  • が、それは生とスマートポインタの両方を使用できるようにIsEqualを書くためにどのような方法がありますか?
  • std::not1を使用して手錠をかけましたか?代わりにIsNotEqualと書いてください。

制限:

  1. 私はブーストライブラリーから何かを使用することはできません。
  2. 私たちのコンパイラは、C++ 0x lambdasをサポートするのに十分ではありません。あなたは->とdereferencableすべてなので、生のポインタとスマートポインタに異なるoperator()のインスタンスを持っています

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

+1

これは、テンプレートがすばらしいと思われる例のようです。 – GWW

+0

@Kristo:あなたのコンパイラは 'std :: begin'のような他のC++ 0xのものを提供するのに十分ですか? –

+0

@Ben、私たちはgcc 4.1.2を使用しています。 'std :: begin'と' 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

'operator() 'にヌルポインタをチェックするための+1。は、私にはよく見えますよ。 –

+0

@Vlad:良い古い普通の配列では動作しません。また、 'unary_function :: Arg'が' operator()() 'のパラメータ型と異なるのは良い考えですか? –

+0

@Ben:私は同じタイプの定数参照については問題ないと思います。配列をサポートするためには、単純な配列の場合は、テンプレート void foo(const(&array)[Len])のような別の特殊化が必要です。 –

2

私のショットは、このようなものになるだろう。

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

ええと、その基本クラスについて... –

+0

できますか?私は、 'unary_function'の最初のテンプレート引数が' operator() 'の引数型と一致しなければならないと考えました。 –

+0

いいえ、できません。はい、そうです。 –

8

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

うーん、私はそれが好きです。しかし、実際には 'IsEqual >'のようなタイピングではありません(そして、私は控除を書く必要はありません)。 +1とにかく。 –

+0

@Kristo:Ok、しかし 'isAllEqual'もテンプレートにして、コードが来ます。 –

+0

'IsEqual'にデフォルトのテンプレート引数を与えることもできます:' template struct IsEqual ... 'そして' IsEqual'を 'Foo *'のイテレータに対して以前と同じように使い続けます。 – aschepler

1

あなたは多分暗黙の変換でトリッキーな何かができる:についてどのように

0

ベンの答えは本当にあなたがC++ 03で行うことができる唯一のものです。しかし、C++ 0xでは、および/またはboost :: bindでは、una​​ry_functionから継承する必要はありません。これにより、templated()演算子を使用できます。あなたは通常、C++ 03で同じことをすることができますが、それは技術的には間違っていると思います。

+0

TR1 'bind'はOKかもしれません。私はコードを使って作業する人たちに確認する必要があります。テンプレート化された 'operator()'を書くのは間違いではありません。それは固執ポイントである 'not1'の使用です。 –

関連する問題