2015-01-28 12 views
5

Vấn đề: Mã sau đây sẽ rất rõ ràng và ngắn gọn, nếu không nhất thiết phải nhanh, không phải là vì nó không biên dịch.Làm thế nào để làm việc xung quanh thực tế là std :: function không có toán tử ==?

Nó không biên dịch vì bạn không thể so sánh std :: trường hợp hàm với toán tử ==(). Và std :: find() cố gắng làm chính xác điều đó.

Tất nhiên, tôi có thể thực hiện một loại triển khai hoàn toàn khác, nhưng cứng đầu như tôi và thích thú với mã bên dưới, tôi đang tìm kiếm thứ "gần nhất có thể" hoạt động.

Ai có thể cung cấp cho tôi một đoạn mã khá viết lại dưới đây cũng thực hiện điều tương tự?

#include <functional> 
#include <vector> 

typedef std::function<bool(int)> Tester_t; 
typedef std::vector<Tester_t> TesterSet_t; 

bool Test(TesterSet_t &candidates, int foo) 
{ 
    TesterSet_t dropouts; 
    for(auto& tester : candidates) 
    { 
     if(!tester(foo))  
     { 
      droputs.push_back(tester); 
     } 
    } 

    while(!dropouts.empty()) 
    { 
     // The following line is not compiling because std::function has no operator==() 
     TesterSet_t::iterator culprit = 
      std::find(candidates.begin(), candidates.end(), dropouts.back()); 
     candidates.erase(culprit); 
     dropouts.pop_back(); 
    } 
    return !candidates.empty(); 
} 
+0

Bạn có thể mô tả vấn đề cơ bản mà bạn đang cố giải quyết không? Lý do 'std :: function' không có toán tử' ==' là bởi vì nó không có nhiều ý nghĩa để so sánh các hàm theo cách đó. Làm thế nào bạn có thể xác định xem hai hàm có bằng nhau không? –

+0

Tương tự (có thể trùng lặp): http://stackoverflow.com/questions/5847072/comparing-stdfunction –

+0

Câu trả lời ngắn: bạn nên tạo cấu trúc chứa khóa duy nhất và hàm 'std ::' và có vectơ là những cấu trúc đó. Nó không có ý nghĩa nhiều khi thực sự sử dụng một 'std :: function' như một khóa tìm kiếm. [Xem tại đây] (http://stackoverflow.com/questions/4430425/stdvector-of-stdfunction) –

Trả lời

10

Như những người khác đã nói, bạn không cần so sánh số std::function cho việc này. Sử dụng tiêu chuẩn C++ cơ sở này có thể là một cách hiệu quả (với độ phức tạp tuyến tính) thực hiện trong hai dòng:

bool Test(TesterSet_t &candidates, int foo) 
{ 
    candidates.erase(std::remove_if(candidates.begin(), candidates.end(), 
     [foo](Tester_t& f){ return !f(foo); }), candidates.end()); 
    return !candidates.empty(); 
} 
+0

Tốt nhất! Tôi chỉ tự hỏi tại sao họ có thể làm điều đó và tôi không thể ?! ;) – BitTickler

+1

Không có so sánh các đối tượng chứa, nó sẽ xóa nếu biến vị ngữ trả về true. –

2

Bạn không cần bình đẳng ở đây. Chỉ cần xóa khi bạn đi

for (auto it = candidates.begin(); it != candidates.end();) { 
    if (! (*it)(foo)) { 
     it = candidates.erase(it); 
    } 
    else { 
     ++it; 
    } 
} 
return !candidates.empty(); 

này cũng sẽ được nhanh hơn so với phiên bản trong câu hỏi thậm chí nếu operator== được định nghĩa cho std::function.

+1

Tẩy xoá trong khi lặp lại là một con đường tốt cho thảm họa. Ít nhất nó đã được như vậy trong nhiều năm. Nhưng có lẽ C++ 11 voodoo làm cho điều này hợp pháp bây giờ? – BitTickler

+4

Việc xóa trong khi lặp lại luôn được thực hiện miễn là bạn thực hiện đúng cách. –

+1

@ user2225104 Rất nhiều thứ dẫn đến thảm họa nếu bạn làm sai. Chỉ cần tìm hiểu đúng cách để làm chúng. – Barry

0

Nếu bạn đã bao giờ không cần phải xóa các ứng cử viên bạn có thể viết:

bool Test(TesterSet_t &candidates, int foo) 
{ 
    return std::any_of(candidates.begin(), candidates.end(), [&foo](Tester_t &tester) { 
     return tester(foo); 
    }); 
} 

CẬP NHẬT

Okay, bạn cần phải loại bỏ các ứng cử viên

bool Test(TesterSet_t &candidates, int foo) 
{ 
    candidates.erase(
     std::remove_if(candidates.begin(), candidates.end(), [&foo](Tester_t &tester) { 
      return !tester(foo); 
     }), 
     candidates.end() 
    ); 
    return !candidates.empty(); 
} 
+0

Việc xóa là một phần của nó. Và cũng có thể, (còn lại) ứng cử viên cần phải có được một đi vào nó. Mã của bạn trả về khi nó tìm thấy mã đầu tiên trả về false. Mục tiêu của mã thực sự là làm mỏng đi bộ ứng cử viên còn lại. – BitTickler

0

Câu trả lời đơn giản là không sử dụng std::function<...> trong trường hợp này mà là một cái gì đó giống như std::function<...> mà không xác định một nhà khai thác bình đẳng. Cách tiếp cận để xác định toán tử bình đẳng cho function<...> là phát hiện khi xây dựng liệu đối tượng hàm thực sự có chứa toán tử bình đẳng không và nếu có, làm cho đối tượng có thể so sánh được. Nếu không, bạn hoặc là sẽ tạo ra một lỗi hoặc bạn sẽ xem xét các đối tượng đang nắm giữ kiểu đối tượng hàm đặc biệt này để không thể so sánh được.

Quan sát ngay lập tức là, tuy nhiên, hầu hết các đối tượng hàm không thể so sánh được! Ví dụ, hàm lambda không thể so sánh được và std::bind()std::mem_fn() cũng không thể tạo ra các đối tượng hàm có thể so sánh được. Một lần nữa, có thể triển khai tùy chỉnh cho std::bind()std::mem_fn(). Không có cách nào để làm cho hàm lambda có thể so sánh được trừ khi có một lần chụp trống trong trường hợp chúng có thể được chuyển thành con trỏ hàm và có thể được so sánh.

Việc thực hiện các đối tượng chức năng nhận thức bình đẳng là một chút quá dài để nhanh chóng nhập vào phản hồi. Tuy nhiên, bạn có thể xem xét triển khai của tôi tại github để so sánh bình đẳng bind()mem_fn(). Xem this answer để triển khai phiên bản so sánh bình đẳng của std::function<...>. Nó có thể được mong muốn để có chức năng lambda có thể so sánh, quá, nếu họ có chữ ký giống nhau và tất cả các giá trị bị bắt là bình đẳng so sánh.

Tất cả những gì đã nói, nếu bạn có thể tránh được sự cần thiết, điều đó có lẽ là tốt nhất nên tránh. Tuy nhiên, tôi đã đi qua một số trường hợp sử dụng khi so sánh std::function<...> sẽ khá tiện dụng mặc dù hạn chế của nó (nghĩa là, không phải tất cả các đối tượng chức năng sẽ được đề cập).

+0

Thật vậy. Đây là lý do tại sao tôi nghĩ rằng từ một thời gian dài rằng cách tiếp cận chức năng auto/lambda/std :: về cơ bản là thiếu sót. Họ thực sự nên giới thiệu một loại nội tại mới và làm cho các giá trị hàm và hàm tham chiếu đến các công dân hạng nhất. Nó không thực sự là vấn đề người dùng C++ cách họ giải quyết việc thực thi ngữ cảnh chụp. Với điều đó tại chỗ, những nỗ lực như C-- có thể đã được lưu lại với nhau. – BitTickler

+0

Vấn đề không phải là quá nhiều để đối phó với việc nắm bắt. Vấn đề là bạn có thể nắm bắt các đối tượng (hoặc tham chiếu đến các đối tượng) mà không phải là sự tương đương bình đẳng. Bạn sẽ muốn có hàm lambda cho các hàm này, ví dụ: khi không cần phải so sánh chúng. Vì vậy, bạn sẽ kết thúc với các đối tượng chức năng không thể so sánh. Làm thế nào để đối phó với họ (xem xét nó là một lỗi/xem xét chúng không bình đẳng) phụ thuộc một chút vào bối cảnh và tôi thực sự thích sự lựa chọn không được nướng vào tiêu chuẩn (tức là, không xác định bình đẳng trong trường hợp này). Tuy nhiên, nếu các đối tượng bị bắt có thể so sánh được, thì sự bình đẳng đối với các hàm lambda sẽ là tốt đẹp. –

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