2012-05-23 43 views
12

Trong C++ 11, làm thế nào để bạn khai báo một hàm nhận dạng biểu thức lambda làm đối số? Tôi có thể tìm thấy nhiều tài nguyên trực tuyến để khai báo lambdas hoặc lấy chúng làm tham số mẫu, nhưng những gì tôi thực sự muốn làm là có thể sử dụng lambdas như trình xử lý gọi lại dễ khai báo, tương tự như những gì có thể thực hiện bằng cách đóng trong JavaScript và các khối mã trong Objective-C.Tham số/loại lưu trữ cho C++ 11 lambda

Về cơ bản, C cổ điển ++ xây dựng Tôi muốn thay thế bằng một lambda là một cái gì đó như:

class MyCallback { 
public: 
    virtual ~MyCallback() {} 
    virtual void operator(int arg) = 0; 
}; 

void registerCallback(const std::shared_ptr<MyCallback> &); 

void foo(void) { 
    int a, b, c; 
    class LocalCallback: public MyCallback { 
     int a, b, c; 
    public: 
     LocalCallback(int a, int b, int c): a(a), b(b), c(c) {} 
     void operator(int arg) { std::cout << (a+b+c)*arg << std::endl; } 
    }; 
    registerCallback(std::shared_ptr<MyCallback>(new LocalCallback(a,b,c))); 
} 

đó sẽ được đơn giản hóa thành:

void registerCallback(/* WHAT GOES HERE? */); 

void foo(void) { 
    int a, b, c; 
    registerCallback([=](int arg){std::cout << (a+b+c)*arg << std::endl; }) 
} 

Vì vậy, những gì diễn ra nơi tôi đã viết /* WHAT GOES HERE? */ ?

CHỈNH SỬA: Mục đích của việc lưu trữ một cuộc gọi lại để được gọi lại sau đó, thay vì được gọi ngay lập tức và được gọi.

+0

Sử dụng các loại địa phương làm đối số mẫu không hợp pháp trong C++ 03. Một số trình biên dịch cho phép nó như là một phần mở rộng mặc dù. – bames53

+2

Lợi thế của một lambda là nó làm cho việc viết lại callbacks dễ dàng hơn. Bạn không cụ thể viết mã sẽ được sử dụng với lambdas, bạn chỉ cần viết mã mà sẽ có một đối số 'function' và người gọi có thể sử dụng (hoặc không) một lambda như họ muốn. –

+0

@ barnes53 Mã tôi đăng không sử dụng loại địa phương làm đối số mẫu. MyCallback là toàn cầu và loại địa phương đang được bao bọc trong tiêu chuẩn :: shared_ptr . Tôi sử dụng mô hình này tại nơi làm việc tất cả các thời gian, đó là lý do tại sao tôi quan tâm đến cách C++ 11 làm cho nó tốt hơn. :) – fluffy

Trả lời

20

Thường là const std::function<void(int)> & hoặc std::function<void(int)>.

Tôi không chắc chắn phán quyết về việc liệu std::function có nên được chuyển bởi tham chiếu const hoặc theo giá trị hay không. Có lẽ bởi giá trị là tốt, đặc biệt là kể từ khi bạn sẽ sao chép nó anyway để lưu trữ.

Trong trường hợp không rõ ràng ở giữa tất cả cú pháp đó, void(int) là một loại hàm và std::function<T> có nghĩa là xấp xỉ ", một hàm có cùng chữ ký với hàm của loại T".

Bản thân Lambdas có vô danh loại. Không có cách nào để đặt tên cho loại biểu thức lambda của bạn, và các loại biểu thức lambda khác nhau với cùng một chữ ký khác nhau:

auto foo = [=](int arg){std::cout << (a+b+c)*arg << std::endl; }; 
auto bar = [=](int arg){std::cout << (a+b+c)*arg << std::endl; }; 
// foo and bar have different types, accessible as decltype(foo), decltype(bar) 

Do đó nhu cầu std::function, mà về cơ bản là một wrapper kiểu xóa để thu thập cùng các functors khác nhau với cùng một chữ ký thành một loại phổ biến. Đó là cầu nối giữa đa hình tĩnh với các mẫu và tính đa hình động mà bạn cần nếu bạn muốn đăng ký gọi lại, lưu trữ nó sau này, và sau đó gọi nó mà không cần phải "nhớ" loại gốc.

+0

Cảm ơn. Như là điển hình, tôi đã đi qua std :: chức năng khoảng 30 giây sau khi gửi câu hỏi của tôi, nhưng câu trả lời của bạn là cách tốt hơn so với những gì tôi đã viết. Cũng chấp nhận nó cho công cụ 'decltype()' và giải thích đầy đủ hơn về cách lambdas và các kiểu hàm hoạt động trong C++ 11. (Hoặc, tôi sẽ chấp nhận nó khi SO cho phép tôi ...) – fluffy

7
void registerCallback(const std::function<void(int)>& callback); 
5

Cân nhắc sử dụng mẫu chức năng. Có rất nhiều lý do tốt để, chẳng hạn như hành vi tốt hơn khi quá tải (quá tải trên std::function là đau đớn):

template<typename Functor> 
void registerCallback(Functor&& functor); 

(Bạn cũng có thể chấp nhận các tham số như Functor functor, điều đó không quá quan trọng.)

Nếu mã cần ví dụ lưu trữ số functor sau, rồi có khả năng sẽ được giữ trong một số std::function. Nơi bạn muốn tránh std::function là trong các thông số chức năng.

+0

Điều này sẽ không yêu cầu việc triển khai phải được hiển thị cho bất kỳ thứ gì sử dụng nó (tức là trong tệp tiêu đề)? Hoặc là C++ 11 cuối cùng có thể giữ các chi tiết thực hiện phương pháp templatized 'ẩn? – fluffy

+0

@fluffy: Vâng, yêu cầu đó đúng. Nhưng điều đó thực sự quan trọng? – ildjarn

+0

@ildjarn Nó không xảy ra nếu tôi không muốn triển khai thực hiện trong một tệp tiêu đề công khai, hoặc nếu tôi muốn đây là một phương thức ảo trên giao diện, chưa kể đến tất cả các điều kiện cạnh khác với thư viện được chia sẻ và các tình huống khác phương pháp templatized chỉ đơn giản là không hoạt động. – fluffy

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