2010-12-13 2 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*의 컨테이너를하고 난 컨테이너의 모든 요소가 있는지 확인하기 위해 std::find_ifstd::not1를 사용하는 경우 주어진 값과 다른 bar() 반환 뭔가. 이번에는 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을 사용할 수 있습니다. 같은 펑터를 두 번 쓰는 것을 피할 수 있다면 더 우아 할 것입니다.

질문 :

  • IsEqual는 그래서 모두 원시와 스마트 포인터를 사용하여 쓸 수있는 방법이 있나요?
  • std::not1을 사용하여 수갑을 채웠습니까? 대신 IsNotEqual을 작성해야합니까?

제한 :

  1. 내가 부스트 라이브러리에서 아무것도를 사용할 수 없습니다.
  2. 우리의 컴파일러는 C++ 0x lambdas를 지원할 정도로 멋지지 않습니다.
+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 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() 인스턴스화를해야합니다.

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 >'(실제로는 deducer를 쓰지 않아도됩니다.)보다 덜 입력하는 것이 아닙니다. 어쨌든 +1. –

+0

@Kristo : 좋습니다.하지만 이제는 isAllEqual을 템플릿으로 만들고, 코드가 생깁니다. –

+0

'IsEqual'에 디폴트 템플릿 인자를 줄 수도 있습니다 :'template struct IsEqual ...'그리고 반복자'Foo * '에 대해 이전처럼'IsEqual'을 계속 사용하십시오. – aschepler

1

당신은 어쩌면 암시 적 변환과 까다로운 뭔가를 할 수 :에 대해 어떻게

0

벤의 대답은 정말로 C++ 03에서 할 수있는 유일한 것입니다. C++ 0x 및/또는 boost :: bind에서는 unary_function으로부터 상속 할 필요가 없습니다. 이렇게하면 templated() 연산자를 사용할 수 있습니다. 대개 C++ 03에서 같은 것을 사용할 수는 있지만 기술적으로 올바르지 않다고 생각합니다.

+0

TR1'bind'가 괜찮을 수도 있습니다. 나는 우리 코드를 다루는 사람들과 확인해야 할 것이다. 그리고 템플릿 화 된'operator()'를 쓰는 것은 잘못된 것이 아닙니다. 그것은 'not1'을 사용하는 것입니다. –

관련 문제