2010-12-13 29 views
8

Do sau:Tôi có thể viết một hàm C++ chấp nhận cả một con trỏ thô và một con trỏ thông minh?

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

Tôi có một container của Foo* và tôi sử dụng std::find_ifstd::not1 để tìm hiểu xem có bất kỳ yếu tố trong container nơi bar() lợi nhuận một cái gì đó khác biệt so với một giá trị nhất định. Mã trông giống như sau:

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

Tua nhanh trong tương lai và bây giờ tôi có một vùng chứa khác, lần này có chứa std::tr1::shared_ptr<Foo>. Tôi rất muốn chỉ đơn giản là tái sử dụng functor của tôi trong một phiên bản quá tải của isAllEqual(). Nhưng tôi không thể. Foo*shared_ptr<Foo> là các loại khác nhau. Và tôi cần phải kế thừa từ unary_function để tôi có thể sử dụng not1. Nó sẽ thanh lịch hơn nếu tôi có thể tránh viết cùng một functor hai lần.

Câu hỏi:

  • Có cách nào để viết IsEqual để nó có thể sử dụng cả hai con trỏ liệu và thông minh?
  • Tôi đã tự còng tay mình bằng cách sử dụng std::not1? Tôi có nên viết IsNotEqual thay thế không?

Hạn chế:

  1. tôi không thể sử dụng bất cứ điều gì từ thư viện boost.
  2. Trình biên dịch của chúng tôi không đủ để hỗ trợ C++ 0x lambdas.
+1

Điều này nghe giống như ví dụ về các mẫu sẽ đẹp. – GWW

+0

@Kristo: Trình biên dịch của bạn có đủ mát để cung cấp các công cụ C++ 0x khác, như 'std :: begin'? –

+0

@Ben, chúng tôi đang sử dụng gcc 4.1.2, vì vậy có lẽ không. 'std :: begin' và' std :: end' không đáng kể để viết. –

Trả lời

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 để kiểm tra con trỏ rỗng trong 'toán tử()'. Co vẻ tôt vơi tôi. –

+0

@Vlad: Không hoạt động với các mảng thông thường cũ tốt. Ngoài ra, nó là một ý tưởng hay cho 'unary_function :: Arg' khác với kiểu tham số của' operator()() '? –

+0

@Ben: Tôi nghĩ rằng để liên tục tham chiếu đến cùng một loại, nó là OK. Để hỗ trợ mảng, tôi đoán cho mảng đơn giản, bạn phải có một chuyên môn khác như mẫu void foo (const (& mảng) [Len]) ... hoặc một cái gì đó tương tự. –

2

bắn của tôi sẽ là một cái gì đó như thế này:

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

Bạn sẽ có một operator() instantiation khác nhau cho tất cả mọi thứ dereferencable với ->, con trỏ nên thô và gợi ý thông minh.

+0

Umm, về lớp cơ sở đó ... –

+0

Bạn có thể làm điều đó? Tôi nghĩ đối số mẫu đầu tiên cho 'unary_function' phải khớp với kiểu đối số của' operator() '. –

+0

Không, bạn không thể. Vâng, đúng vậy. –

8

Làm thế nào về:

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

Hmmm, tôi thích nó. Tuy nhiên, nó không thực sự ít gõ hơn 'IsEqual >' (và sau đó tôi sẽ không phải viết các suy luận). +1 dù sao đi nữa. –

+0

@Kristo: Ok, nhưng bây giờ làm cho 'isAllEqual' là một mẫu, mã sắp tới. –

+0

Bạn cũng có thể cho 'IsEqual' một đối số mẫu mặc định:' template struct IsEqual ... 'Và sau đó tiếp tục sử dụng' IsEqual' như trước cho các trình vòng lặp tới 'Foo *'. – aschepler

1

Bạn có lẽ có thể làm điều gì đó khó khăn với các chuyển đổi ngầm:

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

Nhưng tôi thực sự chứ không phải chỉ viết một IsNotEqual.

0

Câu trả lời của Ben thực sự là điều duy nhất bạn có thể làm trong C++ 03. Trong C++ 0x, và/hoặc với boost :: bind, bạn không cần phải kế thừa từ unary_function. Điều này cho phép bạn sử dụng toán tử templated(). Bạn thường có thể nhận được đi với cùng trong C + + 03 nhưng tôi nghĩ rằng đó là kỹ thuật không chính xác để làm như vậy.

+0

TR1 'bind' có thể là ok. Tôi phải kiểm tra với những người làm việc với mã của chúng tôi. Và nó không phải là không chính xác để viết một toán tử 'templated()'. Đó là việc sử dụng 'not1' đó là điểm gắn bó. –