2010-07-09 37 views
10

Chúng tôi đã cắn lỗi sau nhiều lần:Làm thế nào để tôi so sánh mối quan hệ của con trỏ thành một lỗi?

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

using namespace std; 

void print(int* pn) { cout << *pn << " "; } 

int main() { 
    int* n1 = new int(1); 
    int* n2 = new int(2); 
    int* n3 = new int(3); 

    vector<int*> v; 
    v.push_back(n1); 
    v.push_back(n2); 
    v.push_back(n3); 

    sort(v.begin(), v.end()); // Here be dragons! 

    for_each(v.begin(), v.end(), print); 
    cout << endl; 
    delete n1; delete n2; delete n3; 
} 

Vấn đề là std :: sort được so sánh con trỏ số nguyên không số nguyên, mà không phải là những gì các lập trình viên mong muốn. Tồi tệ hơn, đầu ra có thể xuất hiện chính xác và xác định (xem xét thứ tự các địa chỉ được trả về bởi mới hoặc được cấp phát trên ngăn xếp). Các vấn đề gốc là sắp xếp cuối cùng gọi nhà điều hành < cho T, mà hiếm khi là một ý tưởng tốt khi T là một loại con trỏ.

Có cách nào để ngăn chặn điều này hoặc ít nhất có được cảnh báo trình biên dịch không? Ví dụ, có cách nào để tạo ra một phiên bản tùy chỉnh của std :: sắp xếp rằng yêu cầu một hàm so sánh khi T là một con trỏ?

+1

Chúng tôi đã bị lỗi này cắn nhiều lần. Đó là lý do tại sao tôi quan tâm đến câu hỏi này. Tôi không nghĩ rằng bất cứ ai đã thực sự nứt nó, mặc dù. Những gì chúng ta cần là cái gì đó để đảm bảo loại điều này không biên dịch, vì vậy khi bạn làm điều đó sau khi tất cả-nighter khác, nó không thể có được trái trong mã. –

+0

Mỗi khi tôi cố gắng đưa ra một cái gì đó hữu ích để nói ở đây, tất cả đều nắm bắt để "có được các nhà phát triển thông minh hơn". Sau đó, tôi nhận ra rằng những điều đơn giản như thế này cũng xảy ra với các nhà phát triển thông minh. Chắc chắn lưu giữ các ghi chú (wiki?) Trên các lỗi "được truy cập nhiều nhất" xuất hiện trên mã của bạn và chú ý hơn đến chúng trong quá trình đánh giá mã. Gấp đôi như vậy trên các nhà phát triển cơ sở. – corsiKa

Trả lời

2

Đối với con trỏ nói chung bạn có thể làm điều này:

#include <ctime> 
    #include <vector> 
    #include <cstdlib> 
    #include <algorithm> 
    #include <functional> 
    #include <type_traits> 

    namespace util { 
     struct sort_pointers { 
      bool operator() (int *a, int *b) { 
       return *a < *b; 
      } 
     }; 

     template <typename T, bool is_pointer = !std::tr1::is_pointer<T>::value> 
     struct sort_helper { 
      typedef std::less<T> wont_compare_pointers; 
     }; 

     template <typename T> 
     struct sort_helper<T,false> { 
     }; 

     template <typename Iterator> 
     void sort(Iterator start, Iterator end) 
     { 
      std::sort(start, 
         end, 
         sort_helper 
         < 
          typename Iterator::value_type 
         >::wont_compare_pointers()); 
     } 

     template <typename Iterator, class Func> 
     void sort(Iterator start, Iterator end, Func f) { 
      std::sort(start, end, f); 
     } 
    } 

    int main() { 
     std::vector<int> v1; 
     std::vector<int*> v2; 
     srand(time(0)); 

     for(int i = 0; i < 10; ++i) { 
      v1.push_back(rand()); 
     } 

     util::sort(v1.begin(), v1.end()); 

     for(int i = 0; i < 10; ++i) { 
      v2.push_back(&v1[i]); 
     } 

     /* util::sort(v2.begin(), v2.end()); */ //fails. 
     util::sort(v2.begin(), v2.end(), util::sort_pointers()); 

     return 0; 
    } 

std::tr1::is_pointer là chỉ là những gì nó được gọi trong Visual Studio 2008, nhưng tôi nghĩ Boost có một quá, và biên dịch mới có thể cung cấp nó như std::is_pointer. Tôi chắc rằng ai đó sẽ có thể viết một giải pháp đẹp hơn, nhưng điều này dường như hoạt động.

Nhưng tôi phải nói, tôi đồng ý với bánh răng, không có lý do gì cho điều này, lập trình viên có thể xem liệu đây có phải là một vấn đề và hành động tương ứng hay không.

Addition:

Bạn có thể khái quát nó hơn một chút tôi nghĩ, để tự động chọn một functor rằng dereference con trỏ và sẽ so sánh các giá trị:

namespace util { 
    template <typename T> 
    struct sort_pointers { 
     bool operator() (T a, T b) { 
      return *a < *b; 
     } 
    }; 

    template <typename T, bool is_pointer = !std::tr1::is_pointer<T>::value> 
    struct sort_helper { 
     typedef std::less<T> compare; 
    }; 

    template <typename T> 
    struct sort_helper<T,false> { 
     typedef sort_pointers<T> compare; 
    }; 

    template <typename Iterator> 
    void sort(Iterator start, Iterator end) 
    { 
     std::sort(start, 
        end, 
        sort_helper 
        < 
         typename Iterator::value_type 
        >::compare()); 
    } 
} 

Bằng cách đó bạn không cần phải suy nghĩ về nếu bạn đang cung cấp nó với con trỏ để so sánh hay không, nó sẽ tự động được sắp xếp ra ngoài.

+0

Rất tốt đẹp! Tôi sửa đổi sort_pointers :: op <để trở về "std :: dont_compare_pointers" tương tự như Nicholas để các nhà phát triển được một lỗi biên dịch và buộc phải cung cấp một so sánh. Bây giờ tôi cần phải tạo một phiên bản sửa đổi của STL sử dụng thủ thuật này cho tất cả các so sánh (sắp xếp, bản đồ :: chèn, vv). –

12

IMO, các lập trình viên nên biết rằng std::sort giả định giá trị của các cửa hàng container. Nếu bạn cần một hành vi khác để so sánh, thì bạn cung cấp một hàm so sánh. Ví dụ. (Chưa được kiểm tra):

template<typename T> 
inline bool deref_compare(T* t1, T* t2) { return *t1 < *t2; } 

//... 

std::sort(v.begin(), v.end(), deref_compare<int>); 

Sửa

FWIW, Jacob's answer đến gần để trực tiếp hoàn thành những gì bạn muốn. Có thể có một số cách để khái quát hơn nữa.

+0

Xin lỗi vì tất cả các chỉnh sửa. Tôi cứ nghĩ nó hoạt động và không phải vậy. Tôi chỉ nên gắn bó với câu trả lời ban đầu của tôi bây giờ. – Cogwheel

+0

Vấn đề đôi khi các nhà phát triển quên cung cấp chức năng so sánh. Ví dụ: họ sửa đổi vùng chứa để lưu trữ bằng con trỏ thay vì theo giá trị nhưng quên cập nhật tất cả các cuộc gọi sắp xếp. –

+0

Vâng, đó là lý do tại sao tôi đã xác nhận cho bài đăng của Jacob. :) FWIW, tôi phải tự hỏi liệu bạn sẽ phải nhắc nhở mọi người không sử dụng std :: phân loại thường xuyên như bạn phải nhắc nhở họ phải cẩn thận hơn khi họ làm;) – Cogwheel

2

Tôi không có câu trả lời hay cho con trỏ nói chung, nhưng bạn có thể hạn chế so sánh nếu bạn đang sử dụng con trỏ thông minh thuộc bất kỳ loại nào - ví dụ: boost :: shared_ptr.

#include <boost/shared_ptr.hpp> 
using namespace std; 

template<class T> 
bool operator<(boost::shared_ptr<T> a, boost::shared_ptr<T> b) 
{ 
    return boost::shared_ptr<T>::dont_compare_pointers; 
} 

int main() { 
    boost::shared_ptr<int> A; 
    boost::shared_ptr<int> B; 
    bool i = A < B; 
} 

Output:

In function 'bool operator<(boost::shared_ptr<T>, boost::shared_ptr<T>) [with T = int]': 
t.cpp:15: instantiated from here 
Line 8: error: 'dont_compare_pointers' is not a member of 'boost::shared_ptr<int>' 
compilation terminated due to -Wfatal-errors. 

Vì vậy, bạn có thể sử dụng con trỏ thông minh, hoặc tạo con trỏ wrapper thông minh của riêng bạn. Điều này là rất nặng cho những gì bạn muốn mặc dù, vì vậy nếu bạn tạo một wrapper để phát hiện tình trạng này, tôi khuyên bạn chỉ sử dụng nó trong chế độ gỡ lỗi. Vì vậy, tạo một macro (ugh, tôi biết) và sử dụng nó để khai báo con trỏ.

#ifdef DEBUG 
    #define pointer(x) pointer_wrapper<X> 
#else 
    #define pointer(x) x* 
#endif 

Điều này vẫn yêu cầu các lập trình viên phải sử dụng nó, tất nhiên!

Các vấn đề liên quan