2012-06-25 14 views
5

Tôi đã tái cấu trúc một số mã và thấy có hai vị trí có thể được viết bằng cùng mã ngoại trừ bộ so sánh của một tập là less<double> ở một nơi và greater<double>. Một cái gì đó như:std :: set chọn ít hoặc lớn hơn so sánh tại thời gian chạy

double MyClass::Function1(double val) 
{ 
    std::set<double, less<double> > s; 
    // Do something with s 
} 

double MyClass::Function2(double val) 
{ 
    std::set<double, greater<double> > s; 
    // Do the same thing with s as in Function1 
} 

Vì vậy, tôi nghĩ đến việc thực hiện:

double MyClass::GeneralFunction(double val, bool condition) 
{ 
    if(condition) 
    { 
     // Select greater as comparator 
    } 
    else 
    { 
     // Select less as comparator 
    } 

    set<double, comparator> s; 
    // common code 
} 

Tôi đã thực hiện nó hoạt động bằng cách sử dụng chức năng so sánh tùy chỉnh của tôi, như thế này:

bool my_greater(double lhs, double rhs) 
{ 
    return lhs > rhs; 
} 

bool my_less(double lhs, double rhs) 
{ 
    return lhs < rhs; 
} 

double MyClass::GeneralFunction(double val, bool condition) 
{ 
    typedef bool(*Comparator) (double, double); 
    Comparator comp = &my_less; 
    if (condition) 
    { 
     comp = &my_greater; 
    } 

    std::set<double, Comparator > s(comp); 

    //.... 
} 

Nhưng tôi muốn để sử dụng các ứng dụng tích hợp sẵn. Vấn đề là tôi không biết làm thế nào để khai báo bộ so sánh và gán cho nó các biến vị ngữ được dựng sẵn.

Mọi trợ giúp sẽ được đánh giá cao.

Trả lời

4

Bạn có thực sự cần một tấm séc runtime?

template <class Comp> double MyClass::Function(double val) 
{ 
    std::set<double, Comp > s; 
    // Do something with s 
} 

Thậm chí nếu bạn làm thế, bạn vẫn có thể sử dụng

double MyClass::Function(double val, bool comp) 
{ 
    return comp ? Function<std::less<double> >(val) : Function<std::greater<double> >(val); 
} 
+0

Cảm ơn! Tôi không thực sự cần kiểm tra thời gian chạy, vì vậy tôi có thể chọn bộ so sánh trong người gọi. – MikMik

2

Tại sao không làm

template <typename Compare> 
double MyClass::GeneralFunction(double val) 
{ 
    std::set<double, Compare> s; 

    //.... 
} 

lựa chọn mẫu của tham số chính thức không phải là một cái gì đó C++ xử lý rất tốt. Đẩy càng nhiều càng tốt vào giai đoạn biên dịch bằng cách gọi người cung cấp đối số mẫu.

Sau đó, bạn có thể cung cấp một wrapper, nếu bạn thực sự muốn chọn một trong thời gian chạy:

double MyClass::GeneralFunction(double val, bool condition) 
{ 
    return condition ? 
     GeneralFunction<std::greater<double> >(val) : 
     GeneralFunction<std::less <double> >(val);\ 
} 
3

Chỉ cần sử dụng

std::set<double, std::function<bool(double,double)>> 

như đặt của bạn, và nhanh chóng nó như vậy:

typedef std::set<double, std::function<bool(double,double)> > RTSet; 

RTSet choose_ordering(bool increasing) 
{ 
    if (increasing) 
     return RTSet(std::less<double>()); 
    else 
     return RTSet(std::greater<double>()); 
} 

Note nói chung cân bằng của bạn là một trong hai:

  • kiểm tra đặt hàng trên tất cả các so sánh, hoặc
  • kiểm tra xem nó một lần tại instantiation nhưng sau đó cũng phải chịu một gián tiếp trên mỗi cuộc gọi chức năng (như một cuộc gọi chức năng ảo cho ví dụ)

Tôi thích tùy chọn thứ hai để bạn không thể vô tình thay đổi thứ tự trong khi bộ đang được sử dụng, phá vỡ tất cả các bất biến của nó.


Chỉ cần suy nghĩ nhanh, vì đây có thể là câu trả lời riêng biệt (và thậm chí là câu hỏi), nhưng bạn đề cập hai bit mã giống hệt nhau ngoại trừ thứ tự sắp xếp.

Một thay thế tôi đã sử dụng trong một số trường hợp, là sử dụng một hướng loại duy nhất, và mẫu mã hoạt động trên trường quay (theo loại iterator), do đó bạn có thể làm

if (increasing) 
    do_stuff(set.begin(), set.end()); 
else 
    do_stuff(set.rbegin(), set.rend()); 
+0

+1 cho việc sử dụng 'std :: function'. Liên quan đến nguy cơ vô tình thay đổi thứ tự trong khi thiết lập đang được sử dụng: bất kỳ cờ nào cũng là một thành viên riêng của một đối tượng chức năng, làm giảm nguy cơ rất lớn. –

+0

Phần std :: function là những gì tôi đã thực sự hỏi, nhưng câu trả lời của MSalters là một giải pháp tốt hơn, tôi nghĩ vậy. – MikMik

4

Các vấn đề là bạn không thể chọn loại của bộ so sánh tại thời gian rảnh và std::lessstd::greater có các loại không liên quan. Tương tự, một số std::set được khởi tạo với std::less làm so sánh có loại không liên quan đến trên được khởi tạo với std::greater. Có một số giải pháp có thể, nhưng đơn giản nhất (và là người duy nhất không liên quan đến inhertance khái niệm hàm ảo và phân bổ động) nằm dọc theo dòng của những gì bạn đang làm:

class SelectableCompare 
{ 
    bool myIsGreater; 
public: 
    SelectableCompare(bool isGreater) : myIsGreater(isGreater) {} 
    bool operator()(double d1, double d2) const 
    { 
     static std::less<double> const less; 
     return myIsGreater 
      ? less(d2, d1) 
      : less(d1, d2); 
    } 
}; 

Tôi đã sử dụng tiêu chuẩn std::lessstd::greater vì bạn thể hiện sự quan tâm đến việc làm như vậy. Trong trường hợp của double, đây là, thẳng thắn, quá mức cần thiết; Tôi thường chỉ viết d1 > d2d1 < d2. Tuy nhiên, phiên bản được tô điểm ở trên, có thể có ý nghĩa, vì một số loại có thể có std::less chuyên biệt. Đây cũng là lý do tại sao tôi chỉ sử dụng std::less; nó hoàn toàn có thể hiểu rằng một lập trình viên chỉ chuyên std::less, với kiến ​​thức rằng đây là người duy nhất được sử dụng để đặt hàng trong thư viện chuẩn.

Chỉ cần được hoàn chỉnh: thay thế rõ ràng là sử dụng chiến lược pattern trong so sánh, với một cơ sở so sánh trừu tượng:

class Comparator 
{ 
public: 
    virtual ~Comparator() {} 
    virtual bool isLessThan(double d1, double d2) const = 0; 
}; 

, các lớp học có nguồn gốc khá rõ ràng cho sự so sánh khác nhau, và một wrapper để quản lý bộ nhớ:

class ComparatorWrapper 
{ 
    std::shared_ptr<Comparator> myComparator; 
public: 
    ComparatorWrapper(Comparator* newed_comparator) 
     : myComparator(newed_comparator) 
    { 
    } 
    bool operator()(double d1, double d2) const 
    { 
     return myComparator->isLessThan(d1, d2); 
    } 
}; 

này chắc chắn là quá mức cần thiết cho sự lựa chọn nhị phân bạn cần, nhưng có thể là thích hợp nếu có nhiều sự lựa chọn; ví dụ. a set có thể là được sắp xếp trên một trong nhiều trường khác nhau (tất cả các loại khác nhau).